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;
}
线性混合的效果和效率对比如下。