在这篇文章里,我们一起学习了在OpenCV中如何定义感兴趣区域ROI,如何使用addWeighted函数进行图像混合操作,以及将ROI和addWeighted函数结合起来使用,对指定区域进行图像混合操作。
一、设定感兴趣区域——ROI(region of interest)
在图像处理领域,我们常常要设置感兴趣区域(ROI,region of interest),来专注或者简化我们的工作过程,也就是从图像中选择的一个图像区域,这个区域是我们图像分析关注的重点。我们圈定这个区域,以便进行进一步处理。而且,使用ROI指定我们想读入的目标,可以减少处理时间,增加精度,给图像处理带来不小的便利。
ROI区域定义的两种方法
定义ROI区域有俩种方法,第一种是使用 cv::Rect ,顾名思义,cv::Rect 表示一个矩形区域。指定矩形的左上角坐标(构造函数的前俩个参数) 和矩形的长宽(构函数的后俩个参数)就可以定义一个矩形区域
1 //定义一个Mat类型并给其设定ROI区域
2 Mat imageROI;
3
4 //方法一
5 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));
好了,下面我们来看一个实例,显示如何利用ROI将一幅图加到另一幅图的指定位置。大家如果需要拷贝如下的函数中的代码直接运行的话,自己建一个基于console的程序,然后把函数体中的内容拷贝到main函数中,然后找两幅大小合适的图片,加入到工程目录下,并和代码中读取的文件名一致即可。
在下面的代码中,我们通过一个图像掩膜(mask),直接将插入处的像素设置为logo图像的像素值,这样效果会很赞很逼真
1 /*------------------------------------------------------
2 函数名:ROI_AddImage()
3 描述: 利用感兴趣区域ROI实现图像叠加(把图像叠加到ROI区域中,而不是俩张图的直接叠加)
4 -------------------------------------------------------*/
5
6 #include <opencv2/core/core.hpp>
7 #include <opencv2/highgui/highgui.hpp>
8
9 using namespace cv;
10
11 bool ROI_AddImage()
12 {
13
14 //【1】读入图像
15 Mat srcImage1 = imread("dota_pa.jpg");
16 Mat logoImage = imread("dota_logo.jpg");
17
18 if (!srcImage1.data)
19 {
20 printf("fuck, read the picture is wrong!!!
");
21 return false;
22 }
23
24 if (!logoImage.data)
25 {
26 printf("fuck, read the picture is wrong!!!
");
27 return false;
28 }
29
30 //【2】定义一个Mat类型并给其设定ROI区域
31 Mat imageROI = srcImage1(Rect(200,250,logoImage.cols ,logoImage.rows));
32
33 //【3】加载掩膜
34 Mat mask = imread("dota_logo.jpg",0);
35
36 //【4】将掩膜拷贝到ROI
37 logoImage.copyTo(imageROI,mask);
38
39 //【5】显示结果
40 namedWindow("1 利用ROI实现图像叠加示例窗口");
41 imshow("1 利用ROI实现图像叠加示例窗口",srcImage1);
42
43 return true;
44 }
这个函数首先是载入了两张jpg图片到srcImage1和logoImage中,然后定义了一个Mat类型的imageROI,并使用cv::Rect设置其感兴趣区域为srcImage1中的一块区域,将imageROI和srcImage1关联起来。接着定义了一个Mat类型的的mask并读入dota_logo.jpg,顺势使用Mat:: copyTo把mask中的内容拷贝到imageROI中,于是就得到了最终的效果图,namedWindow和imshow配合使用,显示出最终的结果。
二、初级图像混合——线性混合操作
线性混合操作是一种典型的二元(两个输入)的像素操作,他的理论公式是这样的:
如果看过我之前写的游戏编程Alpha混合那篇文章的朋友们应该有些熟悉,其实他们是差不多的:
【Visual C++】游戏开发五十五浅墨 DirectX教程二十二水乳交融的美学:alpha混合技术
我们通过在范围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);
- 第一个参数,InputArray类型的src1,表示需要加权的第一个数组,常常填一个Mat。
- 第二个参数,alpha,表示第一个数组的权重
- 第三个参数,src2,表示第二个数组,它需要和第一个数组拥有相同的尺寸和通道数。
- 第四个参数,beta,表示第二个数组的权重值。
- 第五个参数,dst,输出的数组,它和输入的两个数组拥有相同的尺寸和通道数。
- 第六个参数,gamma,一个加到权重总和上的标量值。看下面的式子自然会理解。
- 第七个参数,dtype,输出阵列的可选深度,有默认值-1。;当两个输入数组具有相同的深度时,这个参数设置为-1(默认值),即等同于src1.depth()。
如果用数学公式来表达,addWeighted函数计算如下两个数组(src1和src2)的加权和,得到结果输出给第四个参数。即addWeighted函数的作用可以被表示为为如下的矩阵表达式为:
dst = src1[I]*alpha+ src2[I]*beta + gamma;
其中的I,是多维数组元素的索引值。而且,在遇到多通道数组的时候,每个通道都需要独立地进行处理。另外需要注意的是,当输出数组的深度为CV_32S时,这个函数就不适用了,这时候就会内存溢出或者算出的结果压根不对。
理论和函数的讲解就是上面这些,接着我们来看代码实例,以融会贯通。
1 bool LinerBlending()
2 {
3 //【0】定义一些局部变量
4 double alphaValue = 0.5; //这些值感觉不懂
5 double betaValue;
6 Mat srcImage2, srcImage3, dstImage;
7
8 //【1】读取图像(两幅图需为同样的类型和尺寸)
9 srcImage2 = imread("mogu.jpg");
10 srcImage3 = imread("rain.jpg");
11
12 if (!srcImage2.data)
13 {
14 printf("你妹,读取srcImage2错误!!!
");
15 return false;
16 }
17 if (!srcImage3.data)
18 {
19 printf("你妹,读取srcImage3错误!!!
");
20 return false;
21 }
22
23
24 //【2】做图像混合加权操作
25 betaValue = (1.0 - alphaValue );
26 addWeighted(srcImage2,alphaValue ,srcImage3 ,betaValue, 0.0,dstImage );
27
28 //【3】创建并显示原图窗口
29 namedWindow("2 线性混合示例窗口【原图】 by hehhe",1);
30 imshow("2 线性混合示例窗口【原图】 by hehhe", srcImage2);
31
32 //【4】创建并显示效果图窗口
33 namedWindow("3 线性混合示例窗口【效果图】 by hehhe", 1);
34 imshow("3 线性混合示例窗口【效果图】 by hehhe",dstImage);
35
36 return true;
37
38 }
三、综合示例
在前面分别介绍的设定感兴趣区域ROI和使用addWeighted 函数进行线性混合的基础上,我们还将他们俩者中和起来使用,也就是先指定ROI,并用addWeighted 函数对我们指定的ROI区域进行混合操作,我们将其封装在了一个名为ROI_LinerBlending 的函数中。。。。。
1 #include <opencv2/core/core.hpp>
2 #include <opencv2/highgui/highgui.hpp>
3 #include <iostream>
4
5 using namespace cv;
6 using namespace std;
7
8 bool ROI_AddImage();
9 bool LinerBlending();
10 bool ROI_LinerBlending();
11
12
13
14 /*--------------------------------------------------------------------------------------------------
15 【1】 函数名:ROI_AddImage()
16 描述: 利用感兴趣区域ROI实现图像叠加(把图像叠加到ROI区域中,而不是俩张图的直接叠加)
17 ----------------------------------------------------------------------------------------------------*/
18
19
20 bool ROI_AddImage()
21 {
22
23 //【1】读入图像
24 Mat srcImage1 = imread("dota_pa.jpg");
25 Mat logoImage = imread("dota_logo.jpg");
26
27 if (!srcImage1.data)
28 {
29 printf("fuck, read the picture is wrong!!!
");
30 return false;
31 }
32
33 if (!logoImage.data)
34 {
35 printf("fuck, read the picture is wrong!!!
");
36 return false;
37 }
38
39 //【2】定义一个Mat类型并给其设定ROI区域
40 Mat imageROI = srcImage1(Rect(200,250,logoImage.cols ,logoImage.rows));
41
42 //【3】加载掩膜
43 Mat mask = imread("dota_logo.jpg",0);
44
45 //【4】将掩膜拷贝到ROI
46 logoImage.copyTo(imageROI,mask);
47
48 //【5】显示结果
49 namedWindow("1 利用ROI实现图像叠加示例窗口");
50 imshow("1 利用ROI实现图像叠加示例窗口",srcImage1);
51
52 //waitKey();
53
54 return true;
55 }
56
57
58
59 /*----------------------------------------------------
60 【2】 函数名:LinerBlending
61 描述:利用cv::addWeighted() 函数实现图像线性混合
62 -------------------------------------------------------*/
63
64 bool LinerBlending()
65 {
66 //【0】定义一些局部变量
67 double alphaValue = 0.5; //这些值感觉不懂
68 double betaValue;
69 Mat srcImage2, srcImage3, dstImage;
70
71 //【1】读取图像(两幅图需为同样的类型和尺寸)
72 srcImage2 = imread("mogu.jpg");
73 srcImage3 = imread("rain.jpg");
74
75 if (!srcImage2.data)
76 {
77 printf("你妹,读取srcImage2错误!!!
");
78 return false;
79 }
80 if (!srcImage3.data)
81 {
82 printf("你妹,读取srcImage3错误!!!
");
83 return false;
84 }
85
86
87 //【2】做图像混合加权操作
88 betaValue = (1.0 - alphaValue );
89 addWeighted(srcImage2,alphaValue ,srcImage3 ,betaValue, 0.0,dstImage );
90
91 //【3】创建并显示原图窗口
92 namedWindow("2 线性混合示例窗口【原图】 by hehhe",1);
93 imshow("2 线性混合示例窗口【原图】 by hehhe", srcImage2);
94
95 //【4】创建并显示效果图窗口
96 namedWindow("3 线性混合示例窗口【效果图】 by hehhe", 1);
97 imshow("3 线性混合示例窗口【效果图】 by hehhe",dstImage);
98
99 //waitKey();
100
101 return true;
102
103 }
104
105
106
107
108 /*---------------------------------------------------------------------------------------------
109 【3】 函数名:ROI_LinerBlending
110 描述: 线性混合实现函数,指定区域线性图像混合,利用cv::addWeighted ()结合定义
111 感兴趣区域ROI,实现自定义区域的线性混合
112 -----------------------------------------------------------------------------------------------*/
113 bool ROI_LinerBlending()
114 {
115 //【1】读取图像
116 Mat srcImage4 = imread("dota_pa.jpg",1);
117 Mat logoImage = imread("dota_logo.jpg");
118
119 if (!srcImage4.data)
120 {
121 printf("你妹,读取srcImage4错误!!!
");
122 return false;
123 }
124 if (!logoImage.data)
125 {
126 printf("你妹,读取srcImage错误!!!
");
127 return false;
128 }
129
130
131 //【2】定义一个Mat类型并给其设定ROI区域
132 Mat imageROI;
133 imageROI = srcImage4(Rect(200,250,logoImage.cols,logoImage.rows));
134
135 //【3】将logo 加到原图上
136 addWeighted(imageROI,0.5,logoImage,0.3,0,imageROI);
137
138 //【4】显示结果
139 namedWindow("4 区域线性图像混合示例窗口 by hehheh");
140 imshow("4 区域线性图像混合示例窗口 by hehheh",srcImage4);
141
142 return true;
143
144 }
145
146
147 /*------------------------------------------------------
148 【4】 main函数
149 描述:控制台程序的入口函数,我们的程序从这里开始
150 ----------------------------------------------------------*/
151
152 int main()
153 {
154 system("color 10"); //控制cmd 窗口的背景颜色
155
156 if (ROI_AddImage() && LinerBlending() && ROI_LinerBlending())
157 {
158 cout << "嗯,好了,得出了想要的图像" << endl;
159 }
160
161 waitKey();
162 return 0;
163 }
嗯,本篇文章到这里就基本结束了。。。。。。。。。。。。。。