[OpenCV introductory tutorial 5] Separation of color channels & multi-channel image mixing

This series of articles is produced by @浅墨_毛星云, please indicate the source for reprinting. Article link: http://blog.csdn.net/poem_qianmo/article/details/21176257 The OpenCV version used when writing the current blog post: 4.20

In the previous article, we talked about using the addWeighted function for image blending operations, and combining the ROI and addWeighted functions to perform image blending operations on a specified area.

In order to better observe the characteristics of some image materials, sometimes it is necessary to display and adjust the components of the three RGB color channels separately. Through the split and merge methods of OpenCV, the goal can be easily achieved.

This is the main content of our article. Let's look at a screenshot first:
Insert picture description here

One, separate color channels

Let us introduce in detail these two functions that are enemies of each other. The first is the split function for channel separation.

<1>Detailed explanation of split function

Separate a multi-channel array into several single-channel arrays.
ps: The array here is translated into an array or an array according to the context.

The C++ version of this split function has two prototypes, they are:

C++: void split(const Mat& src, Mat*mvbegin);
C++: void split(InputArray m,OutputArrayOfArrays mv);

About variables introduction:

The first parameter, InputArray type m or const Mat& type src, fills in the multi-channel array that we need to separate.

The second parameter, mv of type OutputArrayOfArrays, fills the output array of the function or the output vector container.

Just like the method mentioned in the previous section, the OutputArrayOfArrays here can be found by [Go to Definition] Dafa, and we can find that it is a reference to _OutputArray, then we can see the _OutputArray class again through [Go to Definition] in the source code The prototype is the prototype of OutputArrayOfArrays:

class CV_EXPORTS _OutputArray : public_InputArray
{
public:
   _OutputArray();
 
   _OutputArray(Mat& m);
   template<typename _Tp> _OutputArray(vector<_Tp>& vec);
   template<typename _Tp> _OutputArray(vector<vector<_Tp>>& vec);
   _OutputArray(vector<Mat>& vec);
   template<typename _Tp> _OutputArray(vector<Mat_<_Tp>>& vec);
   template<typename _Tp> _OutputArray(Mat_<_Tp>& m);
   template<typename _Tp, int m, int n> _OutputArray(Matx<_Tp, m,n>& matx);
   template<typename _Tp> _OutputArray(_Tp* vec, int n);
   _OutputArray(gpu::GpuMat& d_mat);
   _OutputArray(ogl::Buffer& buf);
   _OutputArray(ogl::Texture2D& tex);
 
    _OutputArray(constMat& m);
   template<typename _Tp> _OutputArray(const vector<_Tp>&vec);
   template<typename _Tp> _OutputArray(constvector<vector<_Tp> >& vec);
   _OutputArray(const vector<Mat>& vec);
   template<typename _Tp> _OutputArray(const vector<Mat_<_Tp>>& vec);
   template<typename _Tp> _OutputArray(const Mat_<_Tp>& m);
   template<typename _Tp, int m, int n> _OutputArray(constMatx<_Tp, m, n>& matx);
   template<typename _Tp> _OutputArray(const _Tp* vec, int n);
   _OutputArray(const gpu::GpuMat& d_mat);
   _OutputArray(const ogl::Buffer& buf);
   _OutputArray(const ogl::Texture2D& tex);
 
   virtual bool fixedSize() const;
   virtual bool fixedType() const;
   virtual bool needed() const;
   virtual Mat& getMatRef(int i=-1) const;
   /*virtual*/ gpu::GpuMat& getGpuMatRef() const;
   /*virtual*/ ogl::Buffer& getOGlBufferRef() const;
   /*virtual*/ ogl::Texture2D& getOGlTexture2DRef() const;
   virtual void create(Size sz, int type, int i=-1, bool allowTransposed=false,int fixedDepthMask=0) const;
   virtual void create(int rows, int cols, int type, int i=-1, boolallowTransposed=false, int fixedDepthMask=0) const;
   virtual void create(int dims, const int* size, int type, int i=-1, boolallowTransposed=false, int fixedDepthMask=0) const;
   virtual void release() const;
   virtual void clear() const;
 
#ifdefOPENCV_CAN_BREAK_BINARY_COMPATIBILITY
   virtual ~_OutputArray();
#endif
};

There is still a lot of content in the class body. In fact, notice that there are various templates defined in it, and all kinds of overloaded constructors are enough.

Well, after passing through the introduction of OutputArrayOfArrays, we continue to explain split.

The split function splits the multi-channel array into an independent single-channel array. According to the formula, this is the case:
Insert picture description here
Finally, let’s look at an example:

Mat srcImage;
Mat imageROI;
vector<Mat> channels;
srcImage= cv::imread("dota.jpg");
// 把一个3通道图像转换成3个单通道图像
split(srcImage,channels);//分离色彩通道
       imageROI=channels.at(0);
       addWeighted(imageROI(Rect(385,250,logoImage.cols,logoImage.rows)),1.0,
              logoImage,0.5,0.,imageROI(Rect(385,250,logoImage.cols,logoImage.rows)));
 
       merge(channels,srcImage4);
 
       namedWindow("sample");
       imshow("sample",srcImage);

This is probably the content of the split() function that separates a multi-channel array into several single-channel arrays. Let’s take a look at the siblings with him or his deadly opponent-the merge() function.

<2>Detailed explanation of merge function

The function of the merge() function is the reverse operation of the split() function, combining multiple arrays into a multi-channel array.

It combines some given single-channel arrays and merges these isolated single-channel arrays into a multi-channel array, thereby creating a multi-channel array composed of multiple single-channel arrays. It has two C++-based function prototypes:

C++: void merge(const Mat* mv, size_tcount, OutputArray dst)
C++: void merge(InputArrayOfArrays mv,OutputArray dst)

The first parameter, mv, fills in the input matrix or vector container array that needs to be merged. All matrices in this mv parameter must have the same size and depth.

The second parameter, count, when mv is a blank C array, represents the number of input matrices. Obviously, this parameter must be greater than 1.

The third parameter, dst, the output matrix, has the same size and depth as mv[0], and the number of channels is the total number of channels in the matrix array.

Function analysis:

The function of the merge function is to merge some arrays into a multi-channel array. Regarding the details of the combination, each element in the output matrix will be a concatenation of the output array, where the element of the i-th input array is regarded as mv[i]. c generally uses the Mat::at() method to access a channel, that is, channels.at(0) is used in this way.

PS: The Mat::at() method returns a reference to the specified array element. Note that it is a reference, which is equivalent to the two. Modify one of them and the other will change accordingly.

Let's take an example:

vector<Mat> channels;
Mat imageBlueChannel;
Mat imageGreenChannel;
Mat imageRedChannel;
srcImage4= imread("dota.jpg");
// 把一个3通道图像转换成3个单通道图像
split(srcImage4,channels);//分离色彩通道
imageBlueChannel = channels.at(0);
imageGreenChannel = channels.at(1);
imageRedChannel = channels.at(2);

The above code first makes the relevant type declaration, then converts the loaded 3-channel image into 3 single-channel images, puts them in the channels of the vector type, and then performs reference assignment.

According to OpenCV's BGR color space (bule, Green, Red, blue-green-red), channels.at(0) means that the blue component in channels is referenced, and channels.at(1) means that the green part in channels is referenced The color component, channels.at(2), means that the red component in the channels is taken out by reference.

That's it for a pair of plit() function and merge() function and usage that do the opposite operation. In addition, if we need to extract a specific single-channel array from a multi-channel array, or implement some complex channel combinations, we can use the mixChannels() function.

Two, multi-channel image mixing sample program

It is still a detailed annotated sample program that every article will be assigned to you. The knowledge points introduced in this article are used as a carrier to show you the code.

In this article, we encapsulate the implementation code of multi-channel image mixing in a function called MultiChannelBlending(). Just upload the code:

 
//-----------------------------------【程序说明】----------------------------------------------
// 程序名称::【OpenCV入门教程之四】分离颜色通道&多通道图像混合   配套源码
// 操作系统: Windows 10 64bit
// 开发语言: C++
// IDE 版 本:Visual Studio 2019
// OpenCV版本:4.20
//------------------------------------------------------------------------------------------------

//-----------------------------------【头文件包含部分】---------------------------------------
//     描述:包含程序所依赖的头文件
//----------------------------------------------------------------------------------------------                                                                                    
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
//-----------------------------------【命名空间声明部分】---------------------------------------
//     描述:包含程序所使用的命名空间
//-----------------------------------------------------------------------------------------------  
using namespace cv;
using namespace std;
 
 
//-----------------------------------【全局函数声明部分】--------------------------------------
//     描述:全局函数声明
//-----------------------------------------------------------------------------------------------
bool MultiChannelBlending();
 
//-----------------------------------【main( )函数】--------------------------------------------
//     描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main(  )
{
       system("color5E");
 
       if(MultiChannelBlending())
       {
              cout<<endl<<"嗯。好了,得出了你需要的混合值图像~";
       }
 
       waitKey(0);
       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蓝色通道 by浅墨");
       imshow("<1>游戏原画+logo蓝色通道 by浅墨",srcImage);
 
 
       //=================【绿色通道部分】=================
       //     描述:多通道混合-绿色分量部分
       //============================================
 
       //【0】定义相关变量
       Mat  imageGreenChannel;
 
       //【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,注意是引用,相当于两者等价,修改其中一个另一个跟着变
       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绿色通道 by浅墨");
       imshow("<2>游戏原画+logo绿色通道 by浅墨",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红色通道 by浅墨");
       imshow("<3>游戏原画+logo红色通道 by浅墨",srcImage);
 
       return true;
}

It can be found that, in fact, the code in the multi-channel mixing implementation function is roughly divided into three parts, which process the blue, green and red channels respectively. The only difference is that channels.at(0), channels are taken when the channel components are taken. .at(1) is still channels.at(2).

Well, let’s take a look at the screenshot below: Insert picture description hereInsert picture description hereInsert picture description here
————————————————
Copyright notice: This article is the original article of CSDN blogger "浅墨_毛星云", following CC 4.0 BY-SA copyright Agreement, please attach a link to the original source and this statement for reprinting.
Original link: https://blog.csdn.net/poem_qianmo/article/details/21176257

Guess you like

Origin blog.csdn.net/m0_51233386/article/details/114648535