初级图像融合介绍两种方法:
1、加权和操作
2、掩模操作
掩模操作效果优于加权和。
因为是很简单的代码,刚好我电脑有浅墨大大的讲解代码,拷贝做了些修改用于讲解 了。
加权和:
公式:
输出图像 = 融合占比1 * 输入图像1 + 融合占比2 * 输入图像2
1 = 融合占比 + 融合占比
cvAddWeighted函数定义:
CVAPI(void) cvAddWeighted( const CvArr* src1, double alpha,
const CvArr* src2, double beta,
double gamma, CvArr* dst );
作用:计算两个数组(图像阵列)的加权和
参数1:const类的输入图像1
参数2:图像1的融合占比
参数3:const类的输入图像2
参数4:图像2的融合占比
参数5:误差,一般为0
参数6:输出图像
Rect函数定义:
template<typename _Tp> inline
Rect_<_Tp>::Rect_(_Tp _x, _Tp _y, _Tp _width, _Tp _height)
: x(_x), y(_y), width(_width), height(_height) {}
是一个内联类模版的构造函数并做初始化,前面看不懂可以省略,主要看Rect_(_Tp _x, _Tp _y, _Tp _width, _Tp _height)
参数1:x参数
参数2:y参数
参数3:宽度
参数4:长度
注意:x,y参数是以左上角为原点,x,y参数为正数
ROI(感兴趣区域):
方法一:选取原图像设定矩形区域为ROI;
//定义一个Mat类型并给其设定ROI区域 Mat imageROI; //方法一 imageROI=image(Rect(500,250,logo.cols,logo.rows));
方法二:利用索引,起始索引到终止索引
//方法二 imageROI=srcImage3(Range(250,250+logoImage.rows),Range(200,200+logoImage.cols));
行索引 为 行250 到 行(250+logo的行)
列索引 为 列200 到 列(200+logo的列)
这样就完成了坐标系中点(250,200)对整个logo图的索引,ROI也确定
注意:ROI和源图像指向同一内存缓冲区,因此对ROI的操作会直接影响到源图像
加权和操作源代码:
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错误~! "); return false; } if(!srcImage3.data ) { printf("你妹,读取srcImage3错误~! "); return false; } //【2】做图像混合加权操作 betaValue= ( 1.0 - alphaValue ); addWeighted(srcImage2, alphaValue, srcImage3, betaValue, 0.0, dstImage); //【3】创建并显示原图窗口 namedWindow("<2>线性混合示例窗口【原图】 by浅墨", 1); imshow("<2>线性混合示例窗口【原图】 by浅墨", srcImage2 ); namedWindow("<3>线性混合示例窗口【效果图】 by浅墨", 1); imshow("<3>线性混合示例窗口【效果图】 by浅墨", dstImage ); return true; }
思路:
加权和操作缺点:可能伴随像素饱和,如果源图像ROI未与logo图融合之前就已经为最大像素值,把logo图与ROI融合便会超过最大像素值,造成像素饱和
因此采用掩模操作更好
掩模操作源代码:
#include<iostream> #include<opencv2/opencv.hpp> #include<opencv2/highgui.hpp> using namespace std; using namespace cv; int main(int argc, char** argv[]) { //【1】读入图像 Mat srcImage1 = imread("dota_pa.jpg"); Mat logoImage = imread("dota_logo.jpg"); if (!srcImage1.data) { printf("你妹,读取srcImage1错误~! "); return false; } if (!logoImage.data) { printf("你妹,读取logoImage错误~! "); return false; } cout << srcImage1.cols << " " << srcImage1.rows << endl; cout << logoImage.cols << " " << logoImage.rows << endl; //【2】定义一个Mat类型并给其设定ROI区域 Mat imageROI = srcImage1(Rect(600, 0, 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); //【6】将融合图像以设定文件路径写入,注意路径后面加上融合图像的自定义的(名字和格式),但是格式得是opencv所支持的 imwrite("E:\18_List_Code\Opencv_code_puls\ROI\ROI\add.jpg", srcImage1); waitKey(0); return true; }
效果图:
写入的图像:
思路:
copyTo函数定义:
void GpuMat::copyTo(OutputArray dst, InputArray mask) const { copyTo(dst, mask, Stream::Null()); }
例子中便是把logo图像的掩模复制到ROI
logoImage.copyTo(imageROI, mask);
加载掩模过程:定义Mat类变量 以 灰度图格式读取logo图像,并用copyTo函数进行掩模拷贝。
讲解就到这里了,个人认为讲解已经十分详细了,如果还有不懂,请私信我,如果有发现错误,我回及时改正。2017-12-20 22:09:01