1. 程式人生 > >學習OpenCV2——Mat之通道的理解

學習OpenCV2——Mat之通道的理解

too arr end line 簡潔 bgr555 push_back channel 公式

本文詳細介紹了opencv中涉及通道的知識,包括圖像類型轉換,通道合成分解,圖像的顯示。

來源:http://blog.csdn.net/GDFSG/article/details/50927257

1. 知識點

tips1: 一個圖像的通道數是N,就表明每個像素點處有N個數,一個a×b的N通道圖像,其圖像矩陣實際上是b行N×a列的數字矩陣。

OpenCV中圖像的通道可以是1、2、3和4。其中常見的是1通道和3通道,2通道和4通道不常見。

1通道的是灰度圖。

3通道的是彩色圖像,比如RGB圖像。

4通道的圖像是RGBA,是RGB加上一個A通道,也叫alpha通道,表示透明度。PNG圖像是一種典型的4通道圖像。alpha通道可以賦值0到1,或者0到255,表示透明到不透明。

2通道的圖像是RGB555和RGB565。2通道圖在程序處理中會用到,如傅裏葉變換,可能會用到,一個通道為實數,一個通道為虛數,主要是編程方便。RGB555是16位的,2個字節,5+6+5,第一字節的前5位是R,後三位+第二字節是G,第二字節後5位是B,可見對原圖像進行壓縮了。

tips2: OpenCV中用imshow( )來顯示圖像,只要Mat的數據矩陣符合圖像的要求,就可以用imshow來顯示。二通道好像不可以。。。超過了4通道,就不是圖像了,imshow( )也顯示不了。

tips3: imshow( )顯示單通道圖像時一定是灰度圖,如果我們想顯示紅色的R分量,還是應該按三通道圖像顯示,只不過G和B通道要賦值成0或255.

tips4: 通道分解用split( ),通道合成用merg( ),這倆函數都是mixchannel( )的特例。

下面,結合程序說明以上知識點。

2 圖像類型的轉換與顯示

[cpp] view plain copy
  1. Mat image=imread("E:/圖片/color.jpg");
  2. Mat imageGRAY,imageRGBA,imageRGB555;
  3. cvtColor(image,imageGRAY,CV_RGB2GRAY); //RGB轉GRAY
  4. cvtColor(image,imageRGBA,CV_RGB2BGRA); //RGB轉RGBA
  5. cvtColor(image,imageRGB555,CV_RGB2BGR555); //RGB轉RGB555
  6. //來看看通道數
  7. int n = image.channels(); //n=3
  8. int nRGBA = imageRGBA.channels(); //nRGBA = 4
  9. int nRGB555 = imageRGB555.channels(); //nRGB555 = 2
  10. //顯示GRAY、RGB和RGBA圖像
  11. imshow("image",image);
  12. imshow("imageGRAY",imageGRAY);
  13. imshow("imageRGBA",imageRGBA);
  14. //imshow("imageRGB555",imageRGB555); //無法顯示

技術分享圖片


RGB轉GRAY是根據一個心理學公式來的:Gray = R*0.299 + G*0.587 + B*0.114

RGB轉GRBA,默認A通道的數值是255,也就是不透明的。

3 通道的合成與分解

3.1 簡單的例子

我們先來看下最常用的合成與分解函數。

split()

C++: void split(const Mat& mtx, Mat* mv)
C++: void split(const Mat& mtx, vector<Mat>& mv)
C: void cvSplit(const CvArr* src, CvArr* dst0, CvArr* dst1, CvArr* dst2, CvArr* dst3)

參數:mtx 輸入矩陣

mv 輸出矩陣或矩陣數組

src 輸入矩陣

dst0、dst1、dst2、dst3 最多4個單通道的輸出矩陣

當我們要固定提取某個通道的矩陣時,C形式的用法還是蠻實用的,不必向C++用法那樣,先定義vector,再channels.at(k)

[cpp] view plain copy
  1. Mat rgb( 3, 4, CV_8UC3, Scalar(1,2,3,4) );
  2. vector<Mat> channels;
  3. split(rgb,channels);
  4. Mat B = channels.at(0); //從vector中讀數據用vector::at()
  5. Mat G = channels.at(1);
  6. Mat R = channels.at(2);
  7. cout<<"RGB="<<endl<<rgb<<endl;
  8. cout<<"B="<<endl<<B<<endl;
  9. cout<<"G="<<endl<<G<<endl;
  10. cout<<"R="<<endl<<R<<endl;

註意rgb圖像的通道排列是BGR

技術分享圖片

merge( )

C++: void merge(const Mat* mv, size_t count, OutputArray dst)
C++: void merge(const vector<Mat>& mv, OutputArray dst)
C: void cvMerge(const CvArr* src0, const CvArr* src1, const CvArr* src2, const CvArr* src3, CvArr* dst)

參數:mv 輸入矩陣

count 當mv是C形式的array時,count表示輸入矩陣個數

dst 輸出矩陣

src0、src1、src2、src3 最多4個單通道的輸入矩陣

[cpp] view plain copy
  1. Mat R(3,4,CV_8UC1, Scalar(3));
  2. Mat G(3,4,CV_8UC1, Scalar(2));
  3. Mat B(3,4,CV_8UC1, Scalar(1));
  4. Mat RGB( 3, 4, CV_8UC3);
  5. vector<Mat> src;
  6. src.push_back(B); //往vector裏存數據要用vector::push_back()
  7. src.push_back(G);
  8. src.push_back(R);
  9. merge(src,RGB);
  10. cout<<"B="<<endl<<B<<endl;
  11. cout<<"G="<<endl<<G<<endl;
  12. cout<<"R="<<endl<<R<<endl;
  13. cout<<"RGB="<<endl<<RGB<<endl;

技術分享圖片

split( )和merge( )都是mixChannels( )的特例,接下來看看mixChannels()的用法

mixChannels( )

C++: void mixChannels(const Mat* src, int nsrc, Mat* dst, int ndst, const int* fromTo, size_t npairs)
C++: void mixChannels(const vector<Mat>& src, vector<Mat>& dst, const int* fromTo, int npairs)

參數:src 輸入的矩陣,可以是一個矩陣也可以是多個矩陣構成的vector

nsrc 輸入矩陣的個數

dst 輸出矩陣,可以是一個矩陣也可以是多個矩陣構成的vector

ndst 輸出矩陣的個數

fromTo src到dst通道對應數組

npairs fromTo中有幾組對應關系

mixChannels( )本質是改變了幾個通道的順序,輸入一共有幾個通道,輸出肯定也有幾個通道,所以定義fromTo時,要知道有多少個通道,而且通道的編號一定是0,1,2,...

[cpp] view plain copy
  1. Mat RGB(3,4, CV_8UC3,Scalar(1,2,3,4));
  2. Mat A(3,4,CV_8UC1,Scalar(6));
  3. cout<<"RGB="<<endl<<RGB<<endl;
  4. cout<<"A="<<endl<<A<<endl;
  5. //RGB+A合成為RGBA
  6. cout<<"RGB+A合成為RGBA"<<endl;
  7. Mat RGBA(3,4,CV_8UC4);
  8. Mat in[]={RGB,A};
  9. int fromTo1[] = {0,0, 1,1, 2,2, 3,3};
  10. mixChannels(in,2,&RGBA,1,fromTo1,4);
  11. cout<<"RGBA="<<endl<<RGBA<<endl;
  12. //RGB分解為R+GB
  13. cout<<"RGB分解為R+GB"<<endl;
  14. Mat R(3,4,CV_8UC1);
  15. Mat GB(3,4, CV_8UC2);
  16. Mat out[]={R,GB};
  17. int fromTo2[] = {0,2, 1,1, 2,0};
  18. mixChannels(&RGB,1,out,2,fromTo2,3);
  19. cout<<"R="<<endl<<R<<endl;
  20. cout<<"GB="<<endl<<GB<<endl;

技術分享圖片

3.2 以圖像為例

我們先來看一個例子

[cpp] view plain copy
  1. Mat image=imread("E:/圖片/color.jpg");
  2. vector<Mat> channels;
  3. split(image,channels);
  4. Mat B = channels.at(0);
  5. Mat G = channels.at(1);
  6. Mat R = channels.at(2);
  7. imshow("image",image);
  8. imshow("R",R);
  9. imshow("G",G);
  10. imshow("B",B);

技術分享圖片

三個分量R、G、B因為是單通道圖像,所以只能顯示為灰度圖。如果要想顯示出顏色來,應該用三通道圖像來顯示,比如顯示R,我們就讓G和B通道的數值為0或255。看下面例子

[cpp] view plain copy
  1. Mat image=imread("E:/圖片/color.jpg");
  2. vector<Mat> sbgr;
  3. split(image,sbgr); //split to sbgr[0],sbgr[1] ,sbgr[2]
  4. vector<Mat> mbgr(3);
  5. Mat bk1(image.size(),CV_8UC1,Scalar(0));
  6. //Mat bk2(image.size(),CV_8UC1,Scalar(255));
  7. //顯示彩色的B分量
  8. Mat imageB(image.size(),CV_8UC3);
  9. mbgr[0]= sbgr[0];
  10. mbgr[1]= bk1;
  11. mbgr[2]= bk1;
  12. merge(mbgr,imageB);
  13. imshow("imageB",imageB);
  14. //顯示彩色的G分量
  15. Mat imageG(image.size(),CV_8UC3);
  16. mbgr[0]= bk1;
  17. mbgr[1]= sbgr[1];
  18. mbgr[2]= bk1;
  19. merge(mbgr,imageG);
  20. imshow("imageG",imageG);
  21. //顯示彩色的R分量
  22. Mat imageR(image.size(),CV_8UC3);
  23. mbgr[0]= bk1;
  24. mbgr[1]= bk1;
  25. mbgr[2]= sbgr[2];
  26. merge(mbgr,imageR);
  27. imshow("imageR",imageR);
  28. imwrite("imageR.jpg",imageR);
  29. imwrite("imageG.jpg",imageG);
  30. imwrite("imageB.jpg",imageB);
  31. waitKey(0);

技術分享圖片
如果將bk1賦值成255,將會得到下面的圖像

技術分享圖片

4 制作一個透明的圖片

PNG是RGBA的圖片格式,對於一般的RGB圖像,我們只需要加上一個A通道,並且A通道的值不全為1(或255),就可以得到一個透明的圖片。

[cpp] view plain copy
  1. Mat image=imread("E:/圖片/color.jpg");
  2. //定義4種A通道
  3. Mat imageA0 = Mat(image.size(),image.depth(),Scalar(0)); //image.depth()返回0,表示cv_8u
  4. Mat imageA85 = Mat(image.size(),image.depth(),Scalar(85));
  5. Mat imageA170 = Mat(image.size(),image.depth(),Scalar(170));
  6. Mat imageA255 = Mat(image.size(),image.depth(),Scalar(255));
  7. //定義合成後的RGBA圖像,透明度分別為0%,33%,67%,100%,透明---->不透明
  8. Mat imageRGBA0 = Mat(image.size(),CV_8UC4);
  9. Mat imageRGBA85 = Mat(image.size(),CV_8UC4);
  10. Mat imageRGBA170 = Mat(image.size(),CV_8UC4);
  11. Mat imageRGBA255 = Mat(image.size(),CV_8UC4);
  12. Mat in1[] = {image,imageA0};
  13. Mat in2[] = {image,imageA85};
  14. Mat in3[] = {image,imageA170};
  15. Mat in4[] = {image,imageA255};
  16. int from_to[] = {0,0, 1,1, 2,2, 3,3};
  17. mixChannels(in1,2,&imageRGBA0,1,from_to,4);
  18. mixChannels(in2,2,&imageRGBA85,1,from_to,4);
  19. mixChannels(in3,2,&imageRGBA170,1,from_to,4);
  20. mixChannels(in4,2,&imageRGBA255,1,from_to,4);
  21. imwrite("imageRGBA0.png",imageRGBA0);
  22. imwrite("imageRGBA85.png",imageRGBA85);
  23. imwrite("imageRGBA170.png",imageRGBA170);
  24. imwrite("imageRGBA255.png",imageRGBA255);

直接用imshow( )是看不出差別來的,可以這麽看。把一張PPT的空白背景替換成彩色背景,然後把這些 圖片放上去,效果如下圖所示。

技術分享圖片

上圖顯示的結果實質是背景網格圖片和我們的PNG圖片疊加顯示的效果。所以我們有個更簡潔的方法實現上面的顯示效果,是顯示效果!

imageResult = α·image + (1-α)·imageRGID

我們只需要調整α的值就能控制image和imageGRID顯示的比例了。

學習OpenCV2——Mat之通道的理解