11、【opencv入门】ROI区域 mask掩码 图像叠加&初级图像混合
一、设定感兴趣的区域---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 }