轉自http://blog.csdn.net/poem_qianmo/article/details/21176257
本系列文章由@淺墨_毛星雲 出品,轉載請註明出處。
文章鏈接: http://blog.csdn.net/poem_qianmo/article/details/21176257
作者:毛星雲(淺墨) 郵箱: [email protected]
寫作當前博文時配套使用的OpenCV版本: 2.4.8
上篇文章中我們講到了使用addWeighted函數進行圖像混合操作,以及將ROI和addWeighted函數結合起來使用,對指定區域進行圖像混合操作。
而爲了更好的觀察一些圖像材料的特徵,有時需要對RGB三個顏色通道的分量進行分別顯示和調整。通過OpenCV的split和merge方法可以很方便的達到目的。
這就是我們這篇文章的主要內容。依然是先看一張截圖吧:
一、分離顏色通道
就讓我們來詳細介紹一下這兩個互爲冤家的函數。首先是進行通道分離的split函數。
<1>split函數詳解
將一個多通道數組分離成幾個單通道數組。ps:這裏的array按語境譯爲數組或者陣列。
這個split函數的C++版本有兩個原型,他們分別是:
C++: void split(const Mat& src, Mat*mvbegin); C++: void split(InputArray m,OutputArrayOfArrays mv);
關於變量介紹:
- 第一個參數,InputArray類型的m或者const Mat&類型的src,填我們需要進行分離的多通道數組。
- 第二個參數,OutputArrayOfArrays類型的mv,填函數的輸出數組或者輸出的vector容器。
就如上一節中講到方法一樣,這裏的OutputArrayOfArrays我們通過【轉到定義】大法,可以查到它是_OutputArray的引用,那麼我們在源代碼中再次通過【轉到定義】看到_OutputArray類的原型,即是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
- };
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 };
類體中還是有不少內容的,其實注意到裏面是定義的各種模板,重載的各種構造函數就可以了。
好了,穿越完OutputArrayOfArrays的介紹,我們繼續講解split。
split函數分割多通道數組轉換成獨立的單通道數組,按公式來看就是這樣:
最後看一個示例吧:
- 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);
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);
將一個多通道數組分離成幾個單通道數組的split()函數的內容大概就是這些了,下面我們來看一下和他親如手足或者說是他的死對頭——merge()函數。
<2>merge函數詳解
merge()函數的功能是split()函數的逆向操作,將多個數組組合合併成一個多通道的數組。
它通過組合一些給定的單通道數組,將這些孤立的單通道數組合併成一個多通道的數組,從而創建出一個由多個單通道陣列組成的多通道陣列。它有兩個基於C++的函數原型:
- C++: void merge(const Mat* mv, size_tcount, OutputArray dst)
- C++: void merge(InputArrayOfArrays mv,OutputArray dst)
C++: void merge(const Mat* mv, size_tcount, OutputArray dst) 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()方法,返回一個引用到指定的數組元素。注意是引用,相當於兩者等價,修改其中一個另一個跟着變。
來一個示例吧:
- 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);
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);
上面的代碼先做了相關的類型聲明,然後把載入的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()的函數中。直接上代碼吧:
- //-----------------------------------【程序說明】----------------------------------------------
- // 程序名稱::【OpenCV入門教程之四】分離顏色通道&多通道圖像混合 配套源碼
- // VS2010版 OpenCV版本:2.4.8
- // 2014年3月13 日 Create by 淺墨
- // 圖片素材出處:dota2原畫 dota2logo
- // 淺墨的微博:@淺墨_毛星雲
- //------------------------------------------------------------------------------------------------
- //-----------------------------------【頭文件包含部分】---------------------------------------
- // 描述:包含程序所依賴的頭文件
- //----------------------------------------------------------------------------------------------
- #include <cv.h>
- #include <highgui.h>
- #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;
- }
//-----------------------------------【程序說明】---------------------------------------------- // 程序名稱::【OpenCV入門教程之四】分離顏色通道&多通道圖像混合 配套源碼 // VS2010版 OpenCV版本:2.4.8 // 2014年3月13 日 Create by 淺墨 // 圖片素材出處:dota2原畫 dota2logo // 淺墨的微博:@淺墨_毛星雲 //------------------------------------------------------------------------------------------------ //-----------------------------------【頭文件包含部分】--------------------------------------- // 描述:包含程序所依賴的頭文件 //---------------------------------------------------------------------------------------------- #include <cv.h> #include <highgui.h> #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; }
可以發現,其實多通道混合的實現函數中的代碼大體分成三部分,分別對藍綠紅三個通道進行處理,唯一不同的地方是在取通道分量時取的是channels.at(0),channels.at(1)還是channels.at(2)。
嗯,下面看一下運行截圖:
嗯,本篇文章到這裏就基本結束了,最後放出本篇文章配套示例程序的下載地址。
本篇文章的配套源代碼請點擊這裏下載:
OK,本節的內容大概就是這些,我們下篇文章見:)
【OpenCV入門教程之五】 分離顏色通道&多通道圖像混合
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.