ROI区域 图像叠加&图像混合

一、感兴趣区域:ROI

在图像处理领域,我们常常需要设置感兴趣区域 (ROI, region of interest),来专注或者简化工作过程。就是从图像中选择的一个图像区域,这个区域是图像分析所关注的重点。我们圈定这个区域,以便进行进步处理

而且,使用ROI指定想读入的目标,可以减少处理时间,增加精度,给图像处理来带不小的便利。

定义 ROI 区域有两种方法:
第一种是使用表示矩形区域的 Rect。它指定矩形的左上角坐标(构造函数的前两个参数)和矩形的长宽(构造函数的后两个参数)以定义一个矩形区域。

// 其中,image 为已经载入好的图片。
// 定义一个Mat类型并给其设定ROI区域
Mat imageROI;
// 方法一
imageROI = image (Rect (500, 250, logo.cols, logo.rows) );

另一种定义ROI的方式是指定感兴趣行或列的范围( Range)。Range 是指从起始索引到终止索引 (不包括终止索引) 的一连段连续序列。cRange可以用来定义Range。如果使用Range来定义ROI,那么前例中定义ROI的代码可以重写为:

//方法二
imageROI = image (Range(250,250+logoImage.rows)Range(200,200+logoImage.cols));

下面我们来看一个实例,显示如何利用 ROI 将一幅图加到另一幅图的指定位置。

在下面的代码中,我们通过一个图像掩膜 (mask),直接将插入处的像素设置为logo图像的像素值,这样效果会很逼真。

//----------------------------------【ROI_AddImage( )函数】----------------------------------
// 函数名:ROI_AddImage()
//	描述:利用感兴趣区域ROI实现图像叠加
//----------------------------------------------------------------------------------------------
bool  ROI_AddImage()
{

	// 【1】读入图像
	Mat srcImage1= imread("dota_pa.jpg");
	Mat logoImage= imread("dota_logo.jpg");
	if( !srcImage1.data ) { printf("读取srcImage1错误~! \n"); return false; }
	if( !logoImage.data ) { printf("读取logoImage错误~! \n"); return false; }

	// 【2】定义一个Mat类型并给其设定ROI区域
	Mat imageROI= srcImage1(Rect(200,250,logoImage.cols,logoImage.rows));

	// 【3】加载掩模(必须是灰度图)
	Mat mask= imread("dota_logo.jpg",0);

	//【4】将掩膜拷贝到ROI
	logoImage.copyTo(imageROI,mask);

	// 【5】显示结果
	namedWindow("<1>利用ROI实现图像叠加示例窗口");
	imshow("<1>利用ROI实现图像叠加示例窗口",srcImage1);

	return true;
}

这个函数首先是载入了两张 jpg 图片到 srcImage1 和 logoImage 中,然后定义了一个Mat类型的 imageROI,并使用 Rect 设置其感兴趣区域为 srcImage1 中的一块区域,将 imageROI 和 srcImage1 关联起来。接着定义了一个 Mat 类型的 mask 并读入dota_logo.jpg,顺势使用 Mat::copyTo 把 mask 中的内容复制到 imageROI 中,于是就得到了最终的效果图。namedWindow和imshow配合使用,显示出最终的结果。
在这里插入图片描述

二、线性混合操作

线性混合操作是一种典型的二元 (两个输入)的像素操作,它的理论公式如下:

g(x) = (1-a)f,(x)+ af,(x)

我们通过在范围0到1之间改变alpha值,来对 两幅图像(f0(x)和f1(x)) 或 两段视频(同样为f0(x)和f1(x)) 产生时间上的画面叠化( cross dissolve)效果,就像幻灯片放映和电影制作中的那样,也就是在幻灯片翻页时设置的前后页缓慢过渡叠加效果,以及电影情节过渡时经常出现的画面叠加效果。

在实现方面,主要运用了 OpenCV 中 addWeighted 函数, 下面来一起全面地了解一下:

三、计算数组加权和: addWeighted()函数

这个函数的作用是计算两个数组(图像阵列)的加权和。原型如下:

void (InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray dst, int dtype = -1);
  • 第一个参数,InputArray 类型的src1,表示需要加权的第一个数组,常常填一个Mat;
  • 第二个参数,double 类型的alpha,表示第一个数组的权重;
  • 第三个参数,InputArray 类型的sre2,表示第二个数组,它需要和第一个数组拥有相同的尺寸和通道数;
  • 第四个参数,double 类型的beta,表示第二个数组的权重值;
  • 第五个参数,double 类型的gamma,一个加到权重总和上的标量值。其含义通过接下来列出的式子自然会理解;
  • 第六个参数,OutputArray 类型的dst,输出的数组,它和输入的两个数组拥有相同的尺寸和通道数;
  • 第七个参数,int 类型的 dtype,输出阵列的可选深度,有默认值-1。当两个输入数组具有相同的深度时,这个参数设置为-1 (默认值),即等同于src1.depth()

下面的数学公式表示:用 addWeighted 函数计算以下两个数组(src1 和src2) 的加权和,得到结果输出给第四个参数,也就是 addWeighted 函数的作用的矩阵表达式。
dst = src1[I]*alpha + src2[I]*beta + gamma;
其中 I 是多维数组元素的索引值。而且,在遇到多通道数组的时候,每个通道都需要独立地进行处理。另外需要注意的是,当输出数组的深度为CV_32S时,这个函数就不适用了,这时候就会内存溢出或者算出的结果压根不对。

接着我们来看代码实例, 融会贯通。

//---------------------------------【LinearBlending()函数】-------------------------------------
// 函数名:LinearBlending()
// 描述:利用cv::addWeighted()函数实现图像线性混合
//--------------------------------------------------------------------------------------------
bool  LinearBlending()
{
	//【0】定义一些局部变量
	double alphaValue = 0.5; 
	double betaValue;
	Mat srcImage2, srcImage3, dstImage;

	// 【1】读取图像 ( 两幅图片需为同样的类型和尺寸 )
	srcImage2 = imread("mogu.jpg");
	srcImage3 = imread("rain.jpg");

	if( !srcImage2.data ) { printf("读取srcImage2错误! \n"); return false; }
	if( !srcImage3.data ) { printf("读取srcImage3错误! \n"); return false; }

	// 【2】进行图像混合加权操作
	betaValue = ( 1.0 - alphaValue );
	addWeighted( srcImage2, alphaValue, srcImage3, betaValue, 0.0, dstImage);

	// 【3】显示原图窗口
	imshow( "<2>线性混合示例窗口【原图】", srcImage2 );
	imshow( "<3>线性混合示例窗口【效果图】", dstImage );

	return true;

}

在这里插入图片描述
在这里插入图片描述

四、综合示例: 初级图像混合

在前文介绍的设定感兴趣区域ROI和使用addWeighted函数进行图像线性混合的基础上,我们还可以将二者结合起来使用,也就是先指定ROI,再用addWeighted函数对指定的ROI区域的图像进行混合操作。我们将其封装在了一个名为ROI_LinearBlending()的函数中,方便大家分块学习。代码如下。

//---------------------------------【ROI_LinearBlending()】-------------------------------------
// 函数名:ROI_LinearBlending()
// 描述:线性混合实现函数,指定区域线性图像混合.利用cv::addWeighted()函数结合定义
//			  感兴趣区域ROI,实现自定义区域的线性混合
//--------------------------------------------------------------------------------------------
bool  ROI_LinearBlending()
{

	//【1】读取图像
	Mat srcImage4= imread("dota_pa.jpg",1);
	Mat logoImage= imread("dota_logo.jpg");

	if( !srcImage4.data ) { printf("读取srcImage4错误~! \n"); return false; }
	if( !logoImage.data ) { printf("读取logoImage错误~! \n"); return false; }

	//【2】定义一个Mat类型并给其设定ROI区域
	Mat imageROI;
	//方法一
	imageROI= srcImage4(Rect(200,250,logoImage.cols,logoImage.rows));
	//方法二
	//imageROI= srcImage4(Range(250,250+logoImage.rows),Range(200,200+logoImage.cols));

	//【3】将logo加到原图上
	addWeighted(imageROI,0.5,logoImage,0.3,0.,imageROI);

	//【4】显示结果
	imshow("<4>区域线性图像混合示例窗口",srcImage4);

	return true;
}

有了这三个自定义的函数后,完整的main函数如下:

//---------------------------------【头文件、命名空间包含部分】-------------------------------
//		描述:包含程序所使用的头文件和命名空间
//------------------------------------------------------------------------------------------------                                                                                 
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

using namespace cv;
using namespace std;


//-----------------------------------【全局函数声明部分】--------------------------------------
//	描述:全局函数声明
//-----------------------------------------------------------------------------------------------
bool  ROI_AddImage();
bool  LinearBlending();
bool  ROI_LinearBlending();

//-----------------------------------【main( )函数】--------------------------------------------
//	描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main(   )
{
	system("color 6F");

	if(ROI_AddImage( )&& LinearBlending( )&&ROI_LinearBlending( ))
	{
		cout<<endl<<"\n运行成功,得出了需要的图像";
	}

	waitKey(0);
	return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_42815188/article/details/87462681
今日推荐