一、设定感兴趣的区域---ROI(region of interest)
在图像处理领域,我们常常需要设置感兴趣区域(ROI,region of interest),来专注或者简化我们的工作过程 。也就是从图像中选择的一个图像区域,这个区域是我们图像分析所关注的重点。我们圈定这个区域,以便进行进一步处理。而且,使用ROI指定我们想读入的目标,可以减少处理时间,增加精度,给图像处理来带不小的便利。
ROI区域定义的两种方法
定义ROI区域有两种方法,第一种是使用cv:Rect.顾名思义,cv::Rect表示一个矩形区域。指定矩形的左上角坐标(构造函数的前两个参数)和矩形的长宽(构造函数的后两个参数)就可以定义一个矩形区域。
1 //定义一个Mat类型并给其设定ROI区域 2 Mat imageROI; 3 //方法一 4 imageROI=image(Rect(500,250,logo.cols,logo.rows));
另一种定义ROI的方式是指定感兴趣行或列的范围(Range)。Range是指从起始索引到终止索引(不包括终止索引)的一连段连续序列。
cv::Range可以用来定义Range。如果使用cv::Range来定义ROI,那么前例中定义ROI的代码可以重写为:
1 //方法二 2 imageROI=srcImage3(Range(250,250+logoImage.rows),Range(200,200+logoImage.cols));
二、mask掩码
1 mask(掩码或者掩膜):是一个8位单通道图像(灰度图像/二值图像) 2 掩码某个位置如果为0,则在此位置上的操作不起作用 3 掩码某个位置如果不为0,则在此位置上的操作会起作用。 4 可以用来提取不规则ROI
在下面的示例中,我们通过一个图像掩膜(mask),直接将插入处的像素设置为logo图像的像素值,这样效果会很赞很逼真:
1 // 描述:利用感兴趣区域ROI实现图像叠加 2 #include<iostream> 3 #include<opencv2/core/core.hpp> 4 #include<opencv2/highgui/highgui.hpp> 5 6 using namespace std; 7 using namespace cv;//OpenCV中的C++类和函数都是定义在命名空间cv之内的 8 9 int main() 10 { 11 //【1】读入图像 12 Mat srcImage1= imread("poster_dota.jpg"); 13 if(!srcImage1.data ) 14 { 15 cout << "读取错误!" << endl; 16 return false; 17 } 18 19 Mat logoImage= imread("poster_dota_logo.jpg"); 20 if(!logoImage.data ) 21 { 22 cout << "读取错误!" << endl; 23 return false; 24 } 25 26 //【2】定义一个Mat类型并给其设定ROI区域Rect()函数的前两个参数表示左上角的起始坐标,后两个参数表示长方向的长、宽 27 Mat imageROI= srcImage1(Rect(200,250,logoImage.cols,logoImage.rows)); 28 29 //【3】加载掩模(必须是灰度图) 30 Mat mask= imread("dota_logo.jpg",0); 31 32 //【4】将掩膜拷贝到ROI 33 logoImage.copyTo(imageROI,mask); 34 35 //【5】显示结果 36 namedWindow("<1>利用ROI实现图像叠加示例窗口"); 37 imshow("<1>利用ROI实现图像叠加示例窗口",srcImage1); 38 39 waitKey(0);//等待按键触发 40 return true; 41 }
首先是载入了两张jpg图片到srcImage1和logoImage中,然后定义了一个Mat类型的imageROI,并使用cv::Rect设置其感兴趣区域为srcImage1中的一块区域,将imageROI和srcImage1关联起来。
接着定义了一个Mat类型的的mask并读入dota_logo.jpg,顺势使用Mat:: copyTo把mask中的内容拷贝到imageROI中,于是就得到了最终的效果图,namedWindow和imshow配合使用,显示出最终的结果。
这里白色的dota2 logo,就是通过操作之后加上去的图像。
三、初级图像混合 --- 线性混合
线性混合操作是一种典型的二元(两个输入)的像素操作,它的理论公式是这样的:
我们通过在范围0到1之间改变alpha值,来对两幅图像(f0(x)和f1(x))或两段视频(同样为(f0(x)和f1(x))产生时间上的画面叠化(cross-dissolve)效果,就像幻灯片放映和电影制作中的那样。
实现方面,我们主要运用了OpenCV中addWeighted函数。
addWeighted函数
这个函数的作用是,计算两个数组(图像阵列)的加权和。原型如下:
1 void addWeighted(InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray dst, int dtype=-1);
1 第一个参数,InputArray类型的src1,表示需要加权的第一个数组,常常填一个Mat。 2 第二个参数,alpha,表示第一个数组的权重 3 第三个参数,src2,表示第二个数组,它需要和第一个数组拥有相同的尺寸和通道数。 4 第四个参数,beta,表示第二个数组的权重值。 5 第五个参数,gamma,一个加到权重总和上的标量值。看下面的式子自然会理解。 6 第六个参数,dst,输出的数组,它和输入的两个数组拥有相同的尺寸和通道数。 7 第七个参数,dtype,输出阵列的可选深度,有默认值-1。;当两个输入数组具有相同的深度时,这个参数设置为-1(默认值),即等同于src1.depth()。
如果用数学公式来表达,addWeighted函数计算如下两个数组(src1和src2)的加权和,得到结果输出给第四个参数。即addWeighted函数的作用可以被表示为为如下的矩阵表达式为:
dst = src1[I]*alpha+ src2[I]*beta + gamma;
其中的I,是多维数组元素的索引值。而且,在遇到多通道数组的时候,每个通道都需要独立地进行处理。另外需要注意的是,当输出数组的深度为CV_32S时,这个函数就不适用了,这时候就会内存溢出或者算出的结果压根不对。
【示例】
1 //初级图像混合 --- 线性混合 2 #include<iostream> 3 #include<opencv2/core/core.hpp> 4 #include<opencv2/highgui/highgui.hpp> 5 6 using namespace std; 7 using namespace cv;//OpenCV中的C++类和函数都是定义在命名空间cv之内的 8 9 int main() 10 { 11 double alphaValue = 0.5; 12 double betaValue; 13 14 Mat srcImage2, srcImage3, dstImage; 15 16 //读取图像 17 srcImage2 = imread("1.jpg"); 18 if(!srcImage2.data) 19 { 20 cout << "读取错误!" << endl; 21 return false; 22 } 23 srcImage3 = imread("2.jpg"); 24 if(!srcImage3.data) 25 { 26 cout << "读取错误!" << endl; 27 return false; 28 } 29 30 //图像混合加权操作 31 betaValue = (1.0 - alphaValue); 32 addWeighted(srcImage2, alphaValue, srcImage3, betaValue, 0.0, dstImage); 33 34 //创建窗口并显示原图 35 namedWindow("线性混合示例窗口【原图】", 1); 36 imshow("线性混合示例窗口【原图】",srcImage2); 37 38 //创建窗口并显示混合后的图像 39 namedWindow("线性混合示例窗口【效果图】", 1); 40 imshow("线性混合示例窗口【效果图】", dstImage); 41 42 waitKey(0);//等待按键输入 43 return true; 44 }
四、综合示例
在前面分别介绍的设定感兴趣区域ROI和使用addWeighted函数进行图像线性混合的基础上,我们还将他们两者中和起来使用,也就是先指定ROI,并用addWeighted函数对我们指定的ROI区域的图像进行混合操作。
【示例】
1 //综合示例 ROI区域图像叠加 图像线性混合 2 #include<iostream> 3 #include<opencv2/core/core.hpp> 4 #include<opencv2/highgui/highgui.hpp> 5 6 using namespace cv; 7 using namespace std; 8 9 //函数声明 10 bool ROI_AddImage(); 11 bool LinearBlending(); 12 bool ROI_LinearBlending(); 13 14 int main() 15 { 16 system("color 5E"); 17 if(ROI_AddImage() && LinearBlending() && ROI_LinearBlending()) 18 { 19 cout<<endl<<"嗯。好了,得出了你需要的图像~! : )"; 20 } 21 22 waitKey(0); 23 return 0; 24 } 25 26 //ROI图像叠加 27 bool ROI_AddImage() 28 { 29 30 //【1】读入图像 31 Mat srcImage1= imread("dota.jpg"); 32 if(!srcImage1.data ) 33 { 34 cout << "读取错误!" << endl; 35 return false; 36 } 37 38 Mat logoImage= imread("dota_logo.jpg"); 39 if(!logoImage.data ) 40 { 41 cout << "读取错误!" << endl; 42 return false; 43 } 44 45 //【2】方法一:定义一个Mat类型并给其设定ROI区域 46 Mat imageROI= srcImage1(Rect(200,250,logoImage.cols,logoImage.rows)); 47 //方法二:定义一个Mat类型并给其设定ROI区域 48 //Mat imageROI=srcImage1(Range(250,250+logoImage.rows),Range(200,200+logoImage.cols)); 49 50 //【3】加载掩模(必须是灰度图) 51 Mat mask= imread("dota_logo.jpg",0); 52 53 //【4】将掩膜拷贝到ROI 54 logoImage.copyTo(imageROI,mask); 55 56 //【5】显示结果 57 namedWindow("<1>利用ROI实现图像叠加示例窗口"); 58 imshow("<1>利用ROI实现图像叠加示例窗口",srcImage1); 59 //waitKey(0);//等待按键输入 60 return true; 61 } 62 //图像的线性混合 63 bool LinearBlending() 64 { 65 double alphaValue = 0.5; 66 double betaValue; 67 68 Mat srcImage2, srcImage3, dstImage; 69 70 //读取图像 71 srcImage2 = imread("1.jpg"); 72 if(!srcImage2.data) 73 { 74 cout << "读取错误!" << endl; 75 return false; 76 } 77 srcImage3 = imread("2.jpg"); 78 if(!srcImage3.data) 79 { 80 cout << "读取错误!" << endl; 81 return false; 82 } 83 84 //图像混合加权操作 85 betaValue = (1.0 - alphaValue); 86 addWeighted(srcImage2, alphaValue, srcImage3, betaValue, 1.0, dstImage); 87 88 //创建窗口并显示原图 89 namedWindow("线性混合示例窗口【原图】", 1); 90 imshow("线性混合示例窗口【原图】",srcImage2); 91 92 //创建窗口并显示混合后的图像 93 namedWindow("线性混合示例窗口【效果图】", 1); 94 imshow("线性混合示例窗口【效果图】", dstImage); 95 96 //waitKey(0);//等待按键输入 97 return true; 98 } 99 100 //指定区域的线性混合 101 bool ROI_LinearBlending() 102 { 103 //【1】读取图像 104 Mat srcImage4= imread("dota.jpg",1); 105 Mat logoImage= imread("dota_logo.jpg"); 106 107 if(!srcImage4.data ) 108 { 109 cout << "读取错误!" << endl; 110 return false; 111 } 112 if(!logoImage.data ) 113 { 114 cout << "读取错误!" << endl; 115 return false; 116 } 117 118 //【2】定义一个Mat类型并给其设定ROI区域 119 Mat imageROI; 120 //方法一 121 imageROI=srcImage4(Rect(200,250,logoImage.cols,logoImage.rows)); 122 //方法二 123 //imageROI=srcImage4(Range(250,250+logoImage.rows),Range(200,200+logoImage.cols)); 124 125 //【3】将logo加到原图上 126 addWeighted(imageROI,0.5,logoImage,0.3,0.0,imageROI); 127 128 //【4】显示结果 129 namedWindow("<4>ROI区域线性图像混合示例窗口 by浅墨"); 130 imshow("<4>ROI区域线性图像混合示例窗口 by浅墨",srcImage4); 131 //waitKey(0);//等待按键输入 132 return true; 133 }