OpenCV混合(融合)两张图像

  • 一、学习目标
  • 二、图像的线性混合
  • 三、两种方法实现图像的线性混合
  • 四、完整代码示例
  • 五、致谢


一、学习目标

  • 理解什么是两张图像的混合(融合)
  • 使用两种方法实现图像的混合

二、图像的线性混合

在之前的笔记中,我们已经学会了一些基于像素的基本操作。今天来了解一个有趣的二元(双输入)运算符:图像的线性混合运算符。

opencv将两张图片合成一张python opencv两张图片融合_OpenCV教程


α从0→1变化,这个运算可以用于在两个图像或视频之间执行交叉融合,就像幻灯片和电影制作中看到的那样。

注意:执行线性混合的两张图像必须具有同样的尺寸和数据类型。

三、两种方法实现图像的线性混合

1、基于像素点遍历的融合

status addImageByPixel(Mat& src1, Mat& src2, Mat& dst, double alpha, double beta)
{
	CV_Assert(src1.depth() == CV_8U);
	CV_Assert(src2.depth() == CV_8U);
	if (src1.rows != src2.rows || src1.cols != src2.cols || src1.channels() != src2.channels())
	{
		return FALSE;
	}
	int channels = src1.channels();
	for (int row = 0; row < src1.rows; ++row)
	{
		uchar* p1 = src1.ptr<uchar>(row);
		uchar* p2 = src2.ptr<uchar>(row);
		uchar* p3 = dst.ptr<uchar>(row);
		for (int col = 0; col < src1.cols * src1.channels(); ++col)
		{
			p3[col] = saturate_cast<uchar>(alpha * p1[col] + beta * p2[col]);
		}
	}
	return TRUE;
}

使用像素点遍历的逻辑很简单,即取得两张对应图片的像素套用公式即可。注意在调用公式之前判断两张源图片的尺寸是否相等。

2、使用addWeighted() 函数

addWeighted(src1, alpha, src2, 1 - alpha, 0, dst2);

addWeighted() 函数的原型为:

void cv::addWeighted(InputArray   src1,
					 double       alpha,
					 InputArray   src2,
					 double 	  beta,
					 double 	  gamma,
					 OutputArray  dst,
					 int 	      dtype = -1)
  • 参数 src1: 线性混合的第一张图片
  • 参数 alpha: 对应公式中的α
  • 参数 src2: 线性混合的第二张图片
  • 参数 beta: 对应公式中的1-α ,这儿的参数比公式中的设置更灵活
  • 参数 dst: 线性混合后的输出图像
  • 参数 dtype: 可选的输出数组的深度;当两个输入数组具有相同的深度时,dtype可以设置为-1,相当于src1.depth()

四、完整代码示例

#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;

typedef int status;
#define TRUE 1
#define FALSE 0
status addImageByPixel(Mat& src1, Mat& src2, Mat& dst, double alpha, double beta);

int main(int argc, char** argv)
{
	Mat src1, src2;
	string fileName1 = "O:\\CSDN\\2.jpg";
	string fileName2 = "O:\\CSDN\\3.jpg";
    
    // 分别读入两张图片,判断是否读入成功
	src1 = imread(fileName1, IMREAD_COLOR);
	if (!src1.data)
	{
		cerr << "failed to load image :" << fileName1 << "!" << endl;
		system("pause");
		return EXIT_FAILURE;
	}
	src2 = imread(fileName2, IMREAD_COLOR);
	if (src2.empty())
	{
		fprintf(stderr, "failed to load image :%s!\n", fileName2);
		system("pause");
		return EXIT_FAILURE;
	}

	// 初始化输出图像对象
	Mat dst1, dst2;
	dst1.create(src1.size(), src1.type());
	dst2.create(src1.size(), src1.type());
	double alpha = 0.5;

    // 调用基于像素遍历的方法,统计处理时间
	double t1 = (double)getTickCount();
	if (!addImageByPixel(src1, src2, dst1, alpha, 1 - alpha))
	{
		cout << "two images have different size!\n";
		system("pause");
		return EXIT_FAILURE;
	}
	double time1 = ((double)getTickCount() - t1) / getTickFrequency();
	cout << "Method pixel take time:" << time1 << "(ms)" << endl;

    // 调用基于addWeighted函数的方法,统计处理时间
	double t2 = (double)getTickCount();
	addWeighted(src1, alpha, src2, 1 - alpha, 0, dst2);
	double time2 = ((double)getTickCount() - t2) / getTickFrequency();
	cout << "Method addWeighted take time:" << time2 << "(ms)" << endl;

    // 显示混合前和混合后的图像
	imshow("src1", src1);
	imshow("src2", src2);
	imshow("add by pixel", dst1);
	imshow("add by addWeighted", dst2);
	waitKey(0);

	system("pause");
	return EXIT_SUCCESS;
}

status addImageByPixel(Mat& src1, Mat& src2, Mat& dst, double alpha, double beta)
{
	CV_Assert(src1.depth() == CV_8U);
	CV_Assert(src2.depth() == CV_8U);
	// 判断两张图片的size是否一致
	if (src1.rows != src2.rows || src1.cols != src2.cols || src1.channels() != src2.channels())
	{
		return FALSE;
	}
	int channels = src1.channels();
	// 依次处理每一行像素,这儿不需要区分是单通道图像还是多通道图像
	for (int row = 0; row < src1.rows; ++row)
	{
		uchar* p1 = src1.ptr<uchar>(row);
		uchar* p2 = src2.ptr<uchar>(row);
		uchar* p3 = dst.ptr<uchar>(row);
		// 对每一个像素应用公式计算
		for (int col = 0; col < src1.cols * src1.channels(); ++col)
		{
			p3[col] = saturate_cast<uchar>(alpha * p1[col] + beta * p2[col]);
		}
	}
	return TRUE;
}

线性混合的效果和效率对比如下。

opencv将两张图片合成一张python opencv两张图片融合_addWeighted_02