【OpenCV入門教程之五】 分離顏色通道&多通道圖像混合

轉至http://blog.csdn.net/poem_qianmo/article/details/21176257

上篇文章中我們講到了使用addWeighted函數進行圖像混合操作,以及將ROI和addWeighted函數結合起來使用,對指定區域進行圖像混合操作。

而爲了更好的觀察一些圖像材料的特徵,有時需要對RGB三個顏色通道的分量進行分別顯示和調整。通過OpenCV的split和merge方法可以很方便的達到目的。

這就是我們這篇文章的主要內容。依然是先看一張截圖吧:



 

一、分離顏色通道



就讓我們來詳細介紹一下這兩個互爲冤家的函數。首先是進行通道分離的split函數。



<1>split函數詳解


將一個多通道數組分離成幾個單通道數組。ps:這裏的array按語境譯爲數組或者陣列。

 

這個split函數的C++版本有兩個原型,他們分別是:

 

  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的原型:

 

  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函數分割多通道數組轉換成獨立的單通道數組,按公式來看就是這樣:


                             

 

 

 

最後看一個示例吧:

 

  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++的函數原型:

  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()方法,返回一個引用到指定的數組元素。注意是引用,相當於兩者等價,修改其中一個另一個跟着變。

 

來一個示例吧:

 

  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()的函數中。直接上代碼吧:

 

 

  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.        return 0;  
  42. }  
  43.    
  44.    
  45. //-----------------------------【MultiChannelBlending( )函數】--------------------------------  
  46. //     描述:多通道混合的實現函數  
  47. //-----------------------------------------------------------------------------------------------  
  48. bool MultiChannelBlending()  
  49. {  
  50.        //【0】定義相關變量  
  51.        Mat srcImage;  
  52.        Mat logoImage;  
  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.        return true;  
  147. }  


可以發現,其實多通道混合的實現函數中的代碼大體分成三部分,分別對藍綠紅三個通道進行處理,唯一不同的地方是在取通道分量時取的是channels.at(0),channels.at(1)還是channels.at(2)。

嗯,下面看一下運行截圖:







 嗯,本篇文章到這裏就基本結束了,最後放出本篇文章配套示例程序的下載地址。



本篇文章的配套源代碼請點擊這裏下載:



【淺墨OpenCV入門教程之五】配套源代碼下載



OK,本節的內容大概就是這些,我們下篇文章見:)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章