【OpenCV入门教程之五】 分离颜色通道&多通道图像混合

来源:互联网 发布:菲律宾网络博客赌博案 编辑:程序博客网 时间:2024/04/29 09:25

文章链接: http://blog.csdn.net/poem_qianmo/article/details/21176257


上篇文章中我们讲到了使用addWeighted函数进行图像混合操作,以及将ROI和addWeighted函数结合起来使用,对指定区域进行图像混合操作。

而为了更好的观察一些图像材料的特征,有时需要对RGB三个颜色通道的分量进行分别显示和调整。通过OpenCV的split和merge方法可以很方便的达到目的。

这就是我们这篇文章的主要内容。依然是先看一张截图吧:



 

一、分离颜色通道



就让我们来详细介绍一下这两个互为冤家的函数。首先是进行通道分离的split函数。



<1>split函数详解


将一个多通道数组分离成几个单通道数组。ps:这里的array按语境译为数组或者阵列。

 

这个split函数的C++版本有两个原型,他们分别是:

 

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. C++: void split(const Mat& src, Mat*mvbegin);  
  2. C++: void split(InputArray m,OutputArrayOfArrays mv);  


关于变量介绍:

 

  • 第一个参数,InputArray类型的m或者const Mat&类型的src,填我们需要进行分离的多通道数组。
  • 第二个参数,OutputArrayOfArrays类型的mv,填函数的输出数组或者输出的vector容器。

 

 

就如上一节中讲到方法一样,这里的OutputArrayOfArrays我们通过【转到定义】大法,可以查到它是_OutputArray的引用,那么我们在源代码中再次通过【转到定义】看到_OutputArray类的原型,即是OutputArrayOfArrays的原型:

 

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. class CV_EXPORTS _OutputArray : public_InputArray  
  2. {  
  3. public:  
  4.    _OutputArray();  
  5.    
  6.    _OutputArray(Mat& m);  
  7.    template<typename _Tp> _OutputArray(vector<_Tp>& vec);  
  8.    template<typename _Tp> _OutputArray(vector<vector<_Tp>>& vec);  
  9.    _OutputArray(vector<Mat>& vec);  
  10.    template<typename _Tp> _OutputArray(vector<Mat_<_Tp>>& vec);  
  11.    template<typename _Tp> _OutputArray(Mat_<_Tp>& m);  
  12.    template<typename _Tp, int m, int n> _OutputArray(Matx<_Tp, m,n>& matx);  
  13.    template<typename _Tp> _OutputArray(_Tp* vec, int n);  
  14.    _OutputArray(gpu::GpuMat& d_mat);  
  15.    _OutputArray(ogl::Buffer& buf);  
  16.    _OutputArray(ogl::Texture2D& tex);  
  17.    
  18.     _OutputArray(constMat& m);  
  19.    template<typename _Tp> _OutputArray(const vector<_Tp>&vec);  
  20.    template<typename _Tp> _OutputArray(constvector<vector<_Tp> >& vec);  
  21.    _OutputArray(const vector<Mat>& vec);  
  22.    template<typename _Tp> _OutputArray(const vector<Mat_<_Tp>>& vec);  
  23.    template<typename _Tp> _OutputArray(const Mat_<_Tp>& m);  
  24.    template<typename _Tp, int m, int n> _OutputArray(constMatx<_Tp, m, n>& matx);  
  25.    template<typename _Tp> _OutputArray(const _Tp* vec, int n);  
  26.    _OutputArray(const gpu::GpuMat& d_mat);  
  27.    _OutputArray(const ogl::Buffer& buf);  
  28.    _OutputArray(const ogl::Texture2D& tex);  
  29.    
  30.    virtual bool fixedSize() const;  
  31.    virtual bool fixedType() const;  
  32.    virtual bool needed() const;  
  33.    virtual Mat& getMatRef(int i=-1) const;  
  34.    /*virtual*/ gpu::GpuMat& getGpuMatRef() const;  
  35.    /*virtual*/ ogl::Buffer& getOGlBufferRef() const;  
  36.    /*virtual*/ ogl::Texture2D& getOGlTexture2DRef() const;  
  37.    virtual void create(Size sz, int type, int i=-1, bool allowTransposed=false,int fixedDepthMask=0) const;  
  38.    virtual void create(int rows, int cols, int type, int i=-1, boolallowTransposed=falseint fixedDepthMask=0) const;  
  39.    virtual void create(int dims, const int* size, int type, int i=-1, boolallowTransposed=falseint fixedDepthMask=0) const;  
  40.    virtual void release() const;  
  41.    virtual void clear() const;  
  42.    
  43. #ifdefOPENCV_CAN_BREAK_BINARY_COMPATIBILITY  
  44.    virtual ~_OutputArray();  
  45. #endif  
  46. };  



类体中还是有不少内容的,其实注意到里面是定义的各种模板,重载的各种构造函数就可以了。

 

好了,穿越完OutputArrayOfArrays的介绍,我们继续讲解split。

 

split函数分割多通道数组转换成独立的单通道数组,按公式来看就是这样:


                             

 

 

 

最后看一个示例吧:

 

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. Mat srcImage;  
  2. Mat imageROI;  
  3. vector<Mat> channels;  
  4. srcImage= cv::imread("dota.jpg");  
  5. // 把一个3通道图像转换成3个单通道图像  
  6. split(srcImage,channels);//分离色彩通道  
  7.        imageROI=channels.at(0);  
  8.        addWeighted(imageROI(Rect(385,250,logoImage.cols,logoImage.rows)),1.0,  
  9.               logoImage,0.5,0.,imageROI(Rect(385,250,logoImage.cols,logoImage.rows)));  
  10.    
  11.        merge(channels,srcImage4);  
  12.    
  13.        namedWindow("sample");  
  14.        imshow("sample",srcImage);  


将一个多通道数组分离成几个单通道数组的split()函数的内容大概就是这些了,下面我们来看一下和他亲如手足或者说是他的死对头——merge()函数。



<2>merge函数详解


merge()函数的功能是split()函数的逆向操作,将多个数组组合合并成一个多通道的数组。

它通过组合一些给定的单通道数组,将这些孤立的单通道数组合并成一个多通道的数组,从而创建出一个由多个单通道阵列组成的多通道阵列。它有两个基于C++的函数原型:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. C++: void merge(const Mat* mv, size_tcount, OutputArray dst)  
  2. C++: void merge(InputArrayOfArrays mv,OutputArray dst)  


  • 第一个参数,mv,填需要被合并的输入矩阵或vector容器的阵列,这个mv参数中所有的矩阵必须有着一样的尺寸和深度。
  • 第二个参数,count,当mv为一个空白的C数组时,代表输入矩阵的个数,这个参数显然必须大于1.
  • 第三个参数,dst,即输出矩阵,和mv[0]拥有一样的尺寸和深度,并且通道的数量是矩阵阵列中的通道的总数。

 

函数解析:

merge函数的功能是将一些数组合并成一个多通道的数组。关于组合的细节,输出矩阵中的每个元素都将是输出数组的串接,其中,第i个输入数组的元素被视为mv[i]。 c一般用其中的Mat::at()方法对某个通道进行存取,也就是这样用channels.at(0)。

PS: Mat::at()方法,返回一个引用到指定的数组元素。注意是引用,相当于两者等价,修改其中一个另一个跟着变。

 

来一个示例吧:

 

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. vector<Mat> channels;  
  2. Mat imageBlueChannel;  
  3. Mat imageGreenChannel;  
  4. Mat imageRedChannel;  
  5. srcImage4= imread("dota.jpg");  
  6. // 把一个3通道图像转换成3个单通道图像  
  7. split(srcImage4,channels);//分离色彩通道  
  8. imageBlueChannel = channels.at(0);  
  9. imageGreenChannel = channels.at(1);  
  10. imageRedChannel = channels.at(2);  


上面的代码先做了相关的类型声明,然后把载入的3通道图像转换成3个单通道图像,放到vector<Mat>类型的channels中,接着进行引用赋值。

根据OpenCV的BGR色彩空间(bule,Green,Red,蓝绿红),其中channels.at(0)就表示引用取出channels中的蓝色分量,channels.at(1)就表示引用取出channels中的绿色色分量,channels.at(2)就表示引用取出channels中的红色分量。

 

一对做相反操作的plit()函数和merge()函数和用法就是这些了。另外提一点,如果我们需要从多通道数组中提取出特定的单通道数组,或者说实现一些复杂的通道组合,可以使用mixChannels()函数。

 




 

二、多通道图像混合示例程序

 



依然是每篇文章都会配给大家的一个详细注释的示例程序,把这篇文章中介绍的知识点以代码为载体,展现给大家。

 

本篇文章中,我们把多通道图像混合的实现代码封装在了名为MultiChannelBlending()的函数中。直接上代码吧:

 

 

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. //-----------------------------------【程序说明】----------------------------------------------  
  2. //  程序名称::【OpenCV入门教程之四】分离颜色通道&多通道图像混合   配套源码  
  3. // VS2010版   OpenCV版本:2.4.8  
  4. //     2014年3月13 日 Create by 浅墨  
  5. //  图片素材出处:dota2原画 dota2logo   
  6. //     浅墨的微博:@浅墨_毛星云  
  7. //------------------------------------------------------------------------------------------------  
  8.    
  9. //-----------------------------------【头文件包含部分】---------------------------------------  
  10. //     描述:包含程序所依赖的头文件  
  11. //----------------------------------------------------------------------------------------------                                                                                      
  12. #include <cv.h>  
  13. #include <highgui.h>  
  14. #include <iostream>  
  15.    
  16. //-----------------------------------【命名空间声明部分】---------------------------------------  
  17. //     描述:包含程序所使用的命名空间  
  18. //-----------------------------------------------------------------------------------------------    
  19. using namespace cv;  
  20. using namespace std;  
  21.    
  22.    
  23. //-----------------------------------【全局函数声明部分】--------------------------------------  
  24. //     描述:全局函数声明  
  25. //-----------------------------------------------------------------------------------------------  
  26. bool MultiChannelBlending();  
  27.    
  28. //-----------------------------------【main( )函数】--------------------------------------------  
  29. //     描述:控制台应用程序的入口函数,我们的程序从这里开始  
  30. //-----------------------------------------------------------------------------------------------  
  31. int main(  )  
  32. {  
  33.        system("color5E");  
  34.    
  35.        if(MultiChannelBlending())  
  36.        {  
  37.               cout<<endl<<"嗯。好了,得出了你需要的混合值图像~";  
  38.        }  
  39.    
  40.        waitKey(0);  
  41.        return0;  
  42. }  
  43.    
  44.    
  45. //-----------------------------【MultiChannelBlending( )函数】--------------------------------  
  46. //     描述:多通道混合的实现函数  
  47. //-----------------------------------------------------------------------------------------------  
  48. bool MultiChannelBlending()  
  49. {  
  50.        //【0】定义相关变量  
  51.        MatsrcImage;  
  52.        MatlogoImage;  
  53.        vector<Mat>channels;  
  54.        Mat  imageBlueChannel;  
  55.    
  56.        //=================【蓝色通道部分】=================  
  57.        //     描述:多通道混合-蓝色分量部分  
  58.        //============================================  
  59.    
  60.        //【1】读入图片  
  61.        logoImage=imread("dota_logo.jpg",0);  
  62.        srcImage=imread("dota_jugg.jpg");  
  63.    
  64.        if(!logoImage.data ) { printf("Oh,no,读取logoImage错误~!\n"); return false; }  
  65.        if(!srcImage.data ) { printf("Oh,no,读取srcImage错误~!\n"); return false; }  
  66.    
  67.        //【2】把一个3通道图像转换成3个单通道图像  
  68.        split(srcImage,channels);//分离色彩通道  
  69.    
  70.        //【3】将原图的蓝色通道引用返回给imageBlueChannel,注意是引用,相当于两者等价,修改其中一个另一个跟着变  
  71.        imageBlueChannel=channels.at(0);  
  72.        //【4】将原图的蓝色通道的(500,250)坐标处右下方的一块区域和logo图进行加权操作,将得到的混合结果存到imageBlueChannel中  
  73.        addWeighted(imageBlueChannel(Rect(500,250,logoImage.cols,logoImage.rows)),1.0,  
  74.               logoImage,0.5,0,imageBlueChannel(Rect(500,250,logoImage.cols,logoImage.rows)));  
  75.    
  76.        //【5】将三个单通道重新合并成一个三通道  
  77.        merge(channels,srcImage);  
  78.    
  79.        //【6】显示效果图  
  80.        namedWindow("<1>游戏原画+logo蓝色通道 by浅墨");  
  81.        imshow("<1>游戏原画+logo蓝色通道 by浅墨",srcImage);  
  82.    
  83.    
  84.        //=================【绿色通道部分】=================  
  85.        //     描述:多通道混合-绿色分量部分  
  86.        //============================================  
  87.    
  88.        //【0】定义相关变量  
  89.        Mat  imageGreenChannel;  
  90.    
  91.        //【1】重新读入图片  
  92.        logoImage=imread("dota_logo.jpg",0);  
  93.        srcImage=imread("dota_jugg.jpg");  
  94.    
  95.        if(!logoImage.data ) { printf("Oh,no,读取logoImage错误~!\n"); return false; }  
  96.        if(!srcImage.data ) { printf("Oh,no,读取srcImage错误~!\n"); return false; }  
  97.    
  98.        //【2】将一个三通道图像转换成三个单通道图像  
  99.        split(srcImage,channels);//分离色彩通道  
  100.    
  101.        //【3】将原图的绿色通道的引用返回给imageBlueChannel,注意是引用,相当于两者等价,修改其中一个另一个跟着变  
  102.        imageGreenChannel=channels.at(1);  
  103.        //【4】将原图的绿色通道的(500,250)坐标处右下方的一块区域和logo图进行加权操作,将得到的混合结果存到imageGreenChannel中  
  104.        addWeighted(imageGreenChannel(Rect(500,250,logoImage.cols,logoImage.rows)),1.0,  
  105.               logoImage,0.5,0.,imageGreenChannel(Rect(500,250,logoImage.cols,logoImage.rows)));  
  106.    
  107.        //【5】将三个独立的单通道重新合并成一个三通道  
  108.        merge(channels,srcImage);  
  109.    
  110.        //【6】显示效果图  
  111.        namedWindow("<2>游戏原画+logo绿色通道 by浅墨");  
  112.        imshow("<2>游戏原画+logo绿色通道 by浅墨",srcImage);  
  113.    
  114.    
  115.    
  116.        //=================【红色通道部分】=================  
  117.        //     描述:多通道混合-红色分量部分  
  118.        //============================================  
  119.         
  120.        //【0】定义相关变量  
  121.        Mat  imageRedChannel;  
  122.    
  123.        //【1】重新读入图片  
  124.        logoImage=imread("dota_logo.jpg",0);  
  125.        srcImage=imread("dota_jugg.jpg");  
  126.    
  127.        if(!logoImage.data ) { printf("Oh,no,读取logoImage错误~!\n"); return false; }  
  128.        if(!srcImage.data ) { printf("Oh,no,读取srcImage错误~!\n"); return false; }  
  129.    
  130.        //【2】将一个三通道图像转换成三个单通道图像  
  131.        split(srcImage,channels);//分离色彩通道  
  132.    
  133.        //【3】将原图的红色通道引用返回给imageBlueChannel,注意是引用,相当于两者等价,修改其中一个另一个跟着变  
  134.        imageRedChannel=channels.at(2);  
  135.        //【4】将原图的红色通道的(500,250)坐标处右下方的一块区域和logo图进行加权操作,将得到的混合结果存到imageRedChannel中  
  136.        addWeighted(imageRedChannel(Rect(500,250,logoImage.cols,logoImage.rows)),1.0,  
  137.               logoImage,0.5,0.,imageRedChannel(Rect(500,250,logoImage.cols,logoImage.rows)));  
  138.    
  139.        //【5】将三个独立的单通道重新合并成一个三通道  
  140.        merge(channels,srcImage);  
  141.    
  142.        //【6】显示效果图  
  143.        namedWindow("<3>游戏原画+logo红色通道 by浅墨");  
  144.        imshow("<3>游戏原画+logo红色通道 by浅墨",srcImage);  
  145.    
  146.        returntrue;  
  147. }  


可以发现,其实多通道混合的实现函数中的代码大体分成三部分,分别对蓝绿红三个通道进行处理,唯一不同的地方是在取通道分量时取的是channels.at(0),channels.at(1)还是channels.at(2)。

嗯,下面看一下运行截图:







 嗯,本篇文章到这里就基本结束了,最后放出本篇文章配套示例程序的下载地址。



本篇文章的配套源代码请点击这里下载:



【浅墨OpenCV入门教程之五】配套源代码下载

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 元宝鱼烂身子烂鳍尾巴怎么办 大掌门2多余的侠客令怎么办 vivo手机关机后开不了机怎么办 红米手机重置后开机黑屏怎么办 dnf夏日套光环选错了怎么办 ios6微信版本低登录不了怎么办 捡了一个苹果手机有id怎么办 外阴长了个包出血很痛怎么办 腿上烫成泡了泡泡破了掉皮了怎么办 脚上的脚气小泡泡破了怎么办 吃鸡使用改名卡改名符号怎么办 爱派平板电脑密码忘了怎么办 电脑优酷下载总显示未知错误怎么办 文本文档打开时显字符丢失怎么办 系统文件过大无法放进u盘怎么办 淘宝上买的密钥激活不了怎么办 苹果手机玩游戏屏幕卡住不动怎么办 电脑系统安装好一排英文字怎么办 赴日签证申请表写错了怎么办 不知道自己想要做什么工作怎么办 三星note4微信出现闪退怎么办 魅蓝note6手机自动闪退怎么办 苹果6s系统内存占用量过大怎么办 想在一年通过会计初级和中级怎么办 特殊岗位退休档察写的力工怎么办 面试时期望工资说低了。怎么办 面试时期望薪资写低了怎么办 高考后比一模差了80分怎么办 戒了烟我不习惯没有你我怎么办 没有你我不习惯没有你我怎么办 做什么都没兴趣嫌麻烦怎么办 快递还在路上就确认收货了怎么办 微信显示时间与手机不符怎么办 微信提示银行卡预留手机不符怎么办 得了湿疹后吃了海鲜严重了怎么办 看到小区街道乱扔的垃圾你会怎么办 去韩国干服务员不会讲韩语怎么办 华为手机键盘变英文字母大了怎么办 淘宝申请售后卖家余额不足怎么办 发票名称少写了一个字怎么办 微博数量与实际数量不一致怎么办