图像叠加和混合(OpenCV4)

  本篇随笔主要介绍:如何使用OpenCV定义感兴趣区域ROI;如何使用addWeighted函数进行退选哪个混合操作;如何将ROIaddWeighted函数结合来使用,对指定区域图像混合操作。

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;
}
ROI_AddImage

线性混合

  线性混合是典型的二元的像素操作,其理论公式: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;
}
ImageBending
#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;
}
main

  运行结果如下(详见附录):

  

多通道图像混合与分离 

  上述的处理是单通道的图像叠加和混合,接下来将会展示多通道图像的分离和混合。为了更好地观察一些图像处理的特征,有时需要对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;
}
MultiChannelImageBlending

  运行结果如下:
  

参考文献

[1] 毛星云.OpenCV3编程入门[M].电子工业出版社.北京.2015.2.   

 


 

附录

 

 

 

  

 


  

posted @ 2020-10-09 18:02  望星草  阅读(773)  评论(0编辑  收藏  举报