【OpenCV学习】【一】关于图像叠加以及原理解释(结合MATLAB)

时间:2023-02-04 16:51:08

            最近在随浅墨的博客学习OpenCV,受益匪浅,这系列博客记录OpenCV的学习历程,也对自己的学习做一个笔记。

首先感谢浅墨的OpenCV学习系列教程,帮助很大,在此添加浅墨文章的链接。

【OpenCV入门教程之一】

以下结合教程写出关于使用OpenCV实现图像叠加的代码以及一些原理解释,同时结合MATLAB进行学习。

图像的叠加:

由浅入深一步一步学习,首先实现图像叠加,这里添出background图(大图),logo图(小图),实现logo嵌入background任意感兴趣部分,这里有两种方法,先把两种方法的代码添出,再做进一步讨论。先添图片,background和logo。

background:

【OpenCV学习】【一】关于图像叠加以及原理解释(结合MATLAB)

logo:

【OpenCV学习】【一】关于图像叠加以及原理解释(结合MATLAB)

方法一:

//---------------------------------------------------------------------------
// 【程序说明】
//	实现功能:图像叠加
//	重要函数:imread,imshow,namedWindow(可参考浅墨文章)
//			  Rect,Range,copyTo,addWeighted(参考文章分解)
//---------------------------------------------------------------------------
 
#include<iostream>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>

using namespace cv; 

int main( )
{	
	Mat background= imread("dota2.jpg");  
	Mat logo= imread("logo.jpg");  
   
	//载入后先显示  
	namedWindow("background");  
	imshow("background",background);  
  
	namedWindow("logo");  
	imshow("logo",logo);  
	
	Mat imageROI_1;
	//ROI图像与原始图像共享数据
	imageROI_1=background(Rect(200,10,logo.cols,logo.rows));

	namedWindow("imageROI_1");
	imshow("imageROI_1",imageROI_1);//显示ROI

	logo.copyTo(imageROI_1);//覆盖logo

	namedWindow("合成图1");
	imshow("合成图1",background);

	waitKey();
 
	return 0;
}
得到ROI图与合成图:

【OpenCV学习】【一】关于图像叠加以及原理解释(结合MATLAB)



关于代码的头文件包含以及using类的运用,建议查看浅墨文章。这里不作阐述这里主要解析一下

imageROI_1=background(Rect(200,10,logo.cols,logo.rows));

以及

logo.copyTo(imageROI_1);

两段代码。

Rect函数是在图片当中选出ROI图像部分(即你感兴趣的部分),输入图片为background。

Rect(200,10,logo.cols,logo.rows);其中200,10为background图片当中的像素坐标,也是你感兴趣部分(这个部分为一个矩形)的最左上角的点的坐标,而后面两个参数分别为感兴趣部分矩形的横向长度(logo.cols即logo的列数)与竖向长度(logo.rows即logo的行数),我们这里想要选取的感兴趣的矩形与我们要嵌入到background中的logo的大小需要一致。

注:我们可以参考上面添出的ROI图可以看到,ROI图就是从background提出的感兴趣部分矩形。但要注意一点,ROI图经过Rect提出过后,并不是单纯的复制数据,ROI图的数据与background中感兴趣部分矩形的数据是共享的,如果你改变ROI图的数据,background中感兴趣部分矩形的数据也会相应改变,这正是我们下一个函数copyTo想要做的。logo.copyTo(imageROI_1);此函数的输入为logo图片,将logo复制到ROI图片当中,background的相应感兴趣的部分也会相应的改变(正如上面所说的),从而达到图片叠加的效果。

注:文章中的东西都是我通俗的理解,语言比较粗糙。将专业的知识转化成为我们通俗的直感,我一直以来觉得很有必要的,望海涵。

方法二:background与logo都不变,只是选取的区域改变了,代码如下:


//---------------------------------------------------------------------------------

// 【程序说明】
//	实现功能:图像叠加
//	重要函数:imread,imshow,namedWindow(可参考浅墨文章)
//			  Rect,Range,copyTo,addWeighted(参考文章分解)
//---------------------------------------------------------------------------------
 
#include<iostream>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>

using namespace cv; 

int main( )
{	
	Mat background= imread("dota2.jpg");  
	Mat logo= imread("logo.jpg");  
   
	//载入后先显示  
	namedWindow("background");  
	imshow("background",background);  
  
	namedWindow("logo");  
	imshow("logo",logo);  
	
	Mat imageROI_2;
	//ROI图像与原始图像共享数据
	imageROI_2=background(Range(250,250+logo.rows),Range(350,350+logo.cols));
	namedWindow("imageROI_2");
	imshow("imageROI_2",imageROI_2);//显示ROI

	<span style="font-size:24px;">addWeighted(imageROI_2,0.5,logo,0.5,0.,imageROI_2);</span>

	namedWindow("合成图2");
	imshow("合成图2",background);

	waitKey();
 
	return 0;
}
先添出合成图效果:

【OpenCV学习】【一】关于图像叠加以及原理解释(结合MATLAB)

代码和方法一的代码大同小异,只是有两个不同的函数

imageROI_2=background(Range(250,250+logo.rows),Range(350,350+logo.cols));

Range是定义一个范围的数组,从横向和竖向两个方向定义就可以框出一个感兴趣的区域。第一个Range(250,250+logo.rows)定义的是竖向的范围(注意,与Rect定义的顺序不同,Rect是先定义横向),第二个Range(350,350+logo.cols)定义的是横向的范围,以这样的方法也可以选定出自己感兴趣的矩形,很简单。

下面这个函数就是addWeighted,作用是计算两个数组(图像阵列)的加权和:

addWeighted(imageROI_2,0.5,logo,0.5,0.,imageROI_2);

对于这个函数的参数,共有六个(后面用符号代替):

imageROI_2-----------M1

0.5------------------------a1

logo----------------------M2

0.5------------------------a2

0.--------------------------a3

imageROI_2------------M3

这个函数即经过的如下运算:

M3=M1*a1+M2*a2+a3

其中M3为这个函数的输出,M1,M2为Mat类型的数组或是图像阵列,这里M1为ROI图,M2为logo图,a1为ROI图的加权系数,a2为M2的加权系数,a3是一个直流分量,可取0(0.就是0)。

所以你可以看到第二个方法中,嵌入的logo有一定的透明性,是因为logo的加权系数a2没有取到1,如果把a2取到1,a1取0,效果就和方法一copyTo相同了。

想看关于addWeighted更专业的分解,可参考浅墨入门教程四

把两种方法合在一幅图上做做对比:

【OpenCV学习】【一】关于图像叠加以及原理解释(结合MATLAB)


背景透明化

如果我们嵌入的logo图不想要它的背景,比如我们的dota2的logo图片,我们不想要它的白色背景,只要前面的标记,刀塔的字样,那我们需要将logo图片进行预处理,将你不想要的部分全部涂成黑色,便可利用copyTo函数进行透明化处理,我在网上重新下载了logo和background,先贴出来:

【OpenCV学习】【一】关于图像叠加以及原理解释(结合MATLAB)

【OpenCV学习】【一】关于图像叠加以及原理解释(结合MATLAB)

代码如下:

//---------------------------------------------------------------------------------
// 【程序说明】
//	实现功能:图像叠加
//	重要函数:imread,imshow,namedWindow(可参考浅墨文章)
//			  Rect,Range,copyTo,addWeighted(参考文章分解)
//---------------------------------------------------------------------------------
 
#include<iostream>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>

using namespace cv; 

int main( )
{	
	Mat background= imread("pic.jpg");  
	Mat logo= imread("alpha.png");  
        Mat mask=imread("alpha.png",1);
	//载入后先显示  
	namedWindow("background");  
	imshow("background",background);  
  
	namedWindow("logo");  
	imshow("logo",logo);  
	
	Mat imageROI_4;
	//ROI图像与原始图像共享数据
	imageROI_4=background(Rect(40,10,logo.cols,logo.rows));

	namedWindow("imageROI_4");
	imshow("imageROI_4",imageROI_4);//显示ROI

	logo.copyTo(imageROI_4,mask);//覆盖logo

	namedWindow("合成图4");
	imshow("合成图4",background);

	waitKey();
 
	return 0;
}
这个与上面方法一的代码基本一样,但是有两个不同。

多定义了一个mask:

Mat mask=imread("alpha.png",1);

这个mask被称为掩码,是一个标准,后面的参数1在这里有没有都没关系,都是输出原图像,如果你把参数改为0,输出的是这个图像的灰度图。原图与灰度图都可以作为掩码,我这里使用原图作为掩码效果较好,读者可以使用灰度图作为掩码试试。

贴出最后的合成效果图:

【OpenCV学习】【一】关于图像叠加以及原理解释(结合MATLAB)

另一个不同就是copyTo函数这里

logo.copyTo(imageROI_4,mask);

对掩码mask进行了一个使用,这里将logo复制到ROI图当中的时候,首先对mask进行一个判断。mask当中灰度值为0的区域,相对应的ROI当中的区域就不变化,维持原样;当mask当中灰度值不为0的区域,相对应的ROI当中的区域就把logo复制上去。

之所以我们要把我们logo背景全部涂成黑色,就是为了当logo作为mask进行判断时,我们的背景区域不需要,就把它搞成0(黑色),就不会进行操作,维持ROI的原样,而其他的部分,我们用logo复制到ROI中,相应的background就会发生变化(数据共享嘛,前面说过了的)。

以上就是这个第一阶段的OpenCV的学习,先到这里,稍后继续添加内容,以及用MATLAB进行有关实现。