图像叠加和混合(OpenCV4)
本篇随笔主要介绍:如何使用OpenCV定义感兴趣区域ROI;如何使用addWeighted函数进行退选哪个混合操作;如何将ROI和addWeighted函数结合来使用,对指定区域图像混合操作。
Region of interest
定义ROI有两种方法:一是使用矩形区域(Rect),它指定了矩形的左上角坐标(构造函数前两个参数)和矩形的长宽(构造函数的后两个参数)。C/C++描述如下:
// 定义一个Mat类型并给其设定ROI区域 Mat imageROI; // 方法1 imageROI=image(Rect(500,250,logo.cols,logo.rows));
另外一种定义ROI的方式是指定感兴趣的行或列的范围(Range)。代码可重写为:
// 方法二 imageROI=image(Range(250,250+logoImage.rows),Range(200,200+logoImage.cols));
其代码如下:
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; }
线性混合
线性混合是典型的二元的像素操作,其理论公式:g(x)=(1-a)fa(x)+af3(x)。可通过在范围0到1之间改变alpha值来对两幅图像(f0(x)和f1(x))或两段视频产生时间上的画面叠化(cross-dissolve)效果。
addWeighted()函数
这个函数作用是计算两个数组(图像)的加权和。原型如下:
void addWeighted(InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray dst, int dtype = -1); // 表达式: dst=src1[I]*alpha+src2[I]*beta+gamma;
总的演示代码如下:
#pragma once #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <iostream> using namespace cv; using namespace std; //----------------------------------【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; } //---------------------------------【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_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; }
#include "ImageBlending.h" int main() { // system("color 6F"); int end = 0; Mat srcImage1 = imread("dota_pa.jpg"); Mat logoImage = imread("dota_logo.jpg"); imshow("原图1", srcImage1); imshow("原图2", logoImage); if (ROI_AddImage() && LinearBlending() && ROI_LinearBlending()) { cout << "运行成功,得出了需要的图像"; } waitKey(end); return 0; }
运行结果如下(详见附录):
多通道图像混合与分离
上述的处理是单通道的图像叠加和混合,接下来将会展示多通道图像的分离和混合。为了更好地观察一些图像处理的特征,有时需要对RGB三个颜色通道的分量进行分别显示和调整。
【通道分离:split()函数】 split函数可将一个多通道数组分离成几个单通道数组。
C++ : void split(const Mat& src, Mat *mvbegin); C++ : void split(InputArray m, OutputArrayOfArrays mv);
其公式:mv[c](I)=src(I)c。
//【2】把一个3通道图像转换成3个单通道图像 split(srcImage, channels);//分离色彩通道 //【3】将原图的蓝色通道引用返回给imageBlueChannel,注意是引用,相当于两者等价,修改其中一个另一个跟着变 imageBlueChannel = channels.at(0); //【3】将原图的绿色通道的引用返回给imageBlueChannel,注意是引用,相当于两者等价,修改其中一个另一个跟着变 imageGreenChannel = channels.at(1); //【3】将原图的红色通道引用返回给imageBlueChannel,注意是引用,相当于两者等价,修改其中一个另一个跟着变 imageRedChannel = channels.at(2);
【通道合并:merge()函数】 该函数是split()函数的逆向操作——将多个数组合并成一个多通道的数组。
C++ : void merge(const Mat*mv, size_tcount, OutputArray dst) C++ : void merg(InputArrayOfArrays mv, OutputArray dst)
总的演示代码如下:
#include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <iostream> using namespace cv; using namespace std; bool MultiChannelBlending(); int main() { // system("color 9F"); int key = 0; if (MultiChannelBlending()) { cout << "运行成功,得出了需要的图像!"; } waitKey(key); return 0; } //-----------------------------【MultiChannelBlending( )函数】-------------------------------- // 描述:多通道混合的实现函数 //----------------------------------------------------------------------------------------------- bool MultiChannelBlending() { //【0】定义相关变量 Mat srcImage; Mat logoImage; vector<Mat> channels; Mat imageBlueChannel; //=================【蓝色通道部分】================= // 描述:多通道混合-蓝色分量部分 //============================================ // 【1】读入图片 logoImage = imread("dota_logo.jpg", 0); srcImage = imread("dota_jugg.jpg"); if (!logoImage.data) { printf("Oh,no,读取logoImage错误~! \n"); return false; } if (!srcImage.data) { printf("Oh,no,读取srcImage错误~! \n"); return false; } //【2】把一个3通道图像转换成3个单通道图像 split(srcImage, channels);//分离色彩通道 //【3】将原图的蓝色通道引用返回给imageBlueChannel,注意是引用,相当于两者等价,修改其中一个另一个跟着变 imageBlueChannel = channels.at(0); //【4】将原图的蓝色通道的(500,250)坐标处右下方的一块区域和logo图进行加权操作,将得到的混合结果存到imageBlueChannel中 addWeighted(imageBlueChannel(Rect(500, 250, logoImage.cols, logoImage.rows)), 1.0, logoImage, 0.5, 0, imageBlueChannel(Rect(500, 250, logoImage.cols, logoImage.rows))); //【5】将三个单通道重新合并成一个三通道 merge(channels, srcImage); //【6】显示效果图 namedWindow(" <1>游戏原画+logo蓝色通道"); imshow(" <1>游戏原画+logo蓝色通道", srcImage); //=================【绿色通道部分】================= // 描述:多通道混合-绿色分量部分 //============================================ //【0】定义相关变量 Mat imageGreenChannel; //【1】重新读入图片 logoImage = imread("dota_logo.jpg", 0); srcImage = imread("dota_jugg.jpg"); if (!logoImage.data) { printf("读取logoImage错误~! \n"); return false; } if (!srcImage.data) { printf("读取srcImage错误~! \n"); return false; } //【2】将一个三通道图像转换成三个单通道图像 split(srcImage, channels);//分离色彩通道 //【3】将原图的绿色通道的引用返回给imageBlueChannel,注意是引用,相当于两者等价,修改其中一个另一个跟着变 imageGreenChannel = channels.at(1); //【4】将原图的绿色通道的(500,250)坐标处右下方的一块区域和logo图进行加权操作,将得到的混合结果存到imageGreenChannel中 addWeighted(imageGreenChannel(Rect(500, 250, logoImage.cols, logoImage.rows)), 1.0, logoImage, 0.5, 0., imageGreenChannel(Rect(500, 250, logoImage.cols, logoImage.rows))); //【5】将三个独立的单通道重新合并成一个三通道 merge(channels, srcImage); //【6】显示效果图 namedWindow("<2>游戏原画+logo绿色通道"); imshow("<2>游戏原画+logo绿色通道", srcImage); //=================【红色通道部分】================= // 描述:多通道混合-红色分量部分 //============================================ //【0】定义相关变量 Mat imageRedChannel; //【1】重新读入图片 logoImage = imread("dota_logo.jpg", 0); srcImage = imread("dota_jugg.jpg"); if (!logoImage.data) { printf("Oh,no,读取logoImage错误~! \n"); return false; } if (!srcImage.data) { printf("Oh,no,读取srcImage错误~! \n"); return false; } //【2】将一个三通道图像转换成三个单通道图像 split(srcImage, channels);//分离色彩通道 //【3】将原图的红色通道引用返回给imageBlueChannel,注意是引用,相当于两者等价,修改其中一个另一个跟着变 imageRedChannel = channels.at(2); //【4】将原图的红色通道的(500,250)坐标处右下方的一块区域和logo图进行加权操作,将得到的混合结果存到imageRedChannel中 addWeighted(imageRedChannel(Rect(500, 250, logoImage.cols, logoImage.rows)), 1.0, logoImage, 0.5, 0., imageRedChannel(Rect(500, 250, logoImage.cols, logoImage.rows))); //【5】将三个独立的单通道重新合并成一个三通道 merge(channels, srcImage); //【6】显示效果图 namedWindow("<3>游戏原画+logo红色通道 "); imshow("<3>游戏原画+logo红色通道 ", srcImage); return true; }
运行结果如下:
参考文献
[1] 毛星云.OpenCV3编程入门[M].电子工业出版社.北京.2015.2.
附录
【zlc】