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 }
原文地址:https://www.cnblogs.com/Long-w/p/9663140.html