最近一段時間作者開始進行運動目標識別定位系統設計,本文以及後續的幾篇文章都是從一個圖像處理初學者的角度來總結目標檢測定位過程中所應用到的各種常見的算法,尤其是解決算法實現過程中由於粗心大意或者C編程基本功不紮實所引起的各種問題。本文主要對彩色圖片灰度化的方法及其實現過程進行總結,最終給出實現的C代碼。
在進行視頻流目標識別與跟蹤時,通常第一個步驟就是對採集到的彩色圖像進行灰度化,這是因爲黑白照片數據量小,相比彩照更易實現實時算法,另一方面黑白照片是由未處理的光線所形成的照片,因此從圖像處理學角度來看,這種未經特殊濾光處理的圖片所涵蓋的信息更有價值。
目前,在圖像處理過程中,最常用的彩色圖片格式有RGB,HSV、YUV以及HLS三種。以下分別對這三種格式的彩色圖像進行灰度化實現。1、RGB空間圖像
定義於RGB空間的彩色圖,其每個像素點的色彩由R、G、B三個分量共同決定。每個分量在內存所佔的位數共同決定了圖像深度,即每個像素點所佔的字節數。以常見的24深度彩色RGB圖來說,其三個分量各佔1個字節,這樣每個分量可以取值爲0~255,這樣一個像素點可以有1600多萬(255*255*255)的顏色的變化範圍。對這樣一幅彩色圖來說,其對應的灰度圖則是隻有8位的圖像深度(可認爲它是RGB三個分量相等),這也說明了灰度圖圖像處理所需的計算量確實要少。不過需要注意的是,雖然丟失了一些顏色等級,但是從整幅圖像的整體和局部的色彩以及亮度等級分佈特徵來看,灰度圖描述與彩色圖的描述是一致的。
對於RGB圖像進行灰度化,通俗點說就是對圖像的RGB三個分量進行加權平均得到最終的灰度值。最常見的加權方法如下:
1)Gray=B;Gray=G;Gray=R
2)Gray=max(B+G+R)
3)Gray=(B+G+R)/3
4)Gray= 0.072169B+ 0.715160G+ 0.212671R
5)Gray= 0.11B+ 0.59G+ 0.3R這三種方法中,第一種爲分量法,即用RGB三個分量的某一個分量作爲該點的灰度值;第二種方法爲最大值法,將彩色圖像中的三分量亮度的最大值作爲灰度圖的灰度值。第三種方法將彩色圖像中的三分量亮度求平均得到一個灰度圖;後兩種都是屬於加權平均法,其中第四種是OpenCV開放庫所採用的灰度權值,第五種爲從人體生理學角度所提出的一種權值(人眼對綠色的敏感最高,對藍色敏感最低)。
2、其他顏色空間的灰度化
關於HSV以及HLS顏色空間的彩色圖灰度化,可以參考網頁《HSL和HSV色彩空間》,該網頁中所述方法可將幾種不同顏色表達方式進行轉換,將其轉換到RGB空間,然後再採用上述公式進行灰度化。
關於YUV空間的彩色圖像,其Y的分量的物理意義本身就是像素點的亮度,由該值反映亮度等級,因此可根據RGB和YUV顏色空間的變化關係建立亮度Y與R、G、B三個顏色分量的對應:Y=0.3R+0.59G+0.11B,以這個亮度值表達圖像的灰度值。
3、代碼實現
本文旨在對整個實現原理及思路進行總結,因此以下基於OpenCv的基本函數實現這幾種變化過程,至於位圖以及其他形式的圖像,在獲取了圖像原始數據後,處理方法都一樣,僅需注意指針的位置操作即可。具體代碼如下:
- IplImage *ColorImage; //定義相應的圖像指針
- IplImage *GrayImage1; //從1~5代表5中不同權值的結果
- IplImage *GrayImage2;
- IplImage *GrayImage3;
- IplImage *GrayImage4;
- IplImage *GrayImage5;
- IplImage *GrayImage6;
- IplImage *GrayImage7;
- ColorImage = cvLoadImage( "49138.jpg", -1 ); //讀取圖片
- if (ColorImage == NULL)
- return;
- GrayImage1 = cvCreateImage(cvGetSize(ColorImage),8,1);
- GrayImage2 = cvCreateImage(cvGetSize(ColorImage),8,1);
- GrayImage3 = cvCreateImage(cvGetSize(ColorImage),8,1);
- GrayImage4 = cvCreateImage(cvGetSize(ColorImage),8,1);
- GrayImage5 = cvCreateImage(cvGetSize(ColorImage),8,1);
- GrayImage6 = cvCreateImage(cvGetSize(ColorImage),8,1);
- GrayImage7 = cvCreateImage(cvGetSize(ColorImage),8,1);
- CvMat* pGrayMat1 = NULL; //定義與圖像關聯的數據指針
- CvMat* pGrayMat2 = NULL;
- CvMat* pGrayMat3 = NULL;
- CvMat* pGrayMat4 = NULL;
- CvMat* pGrayMat5 = NULL;
- CvMat* pGrayMat6 = NULL;
- CvMat* pGrayMat7 = NULL;
- pGrayMat1 = cvCreateMat(ColorImage->height, ColorImage->width, CV_32FC1);
- pGrayMat2 = cvCreateMat(ColorImage->height, ColorImage->width, CV_32FC1);
- pGrayMat3 = cvCreateMat(ColorImage->height, ColorImage->width, CV_32FC1);
- pGrayMat4 = cvCreateMat(ColorImage->height, ColorImage->width, CV_32FC1);
- pGrayMat5 = cvCreateMat(ColorImage->height, ColorImage->width, CV_32FC1);
- pGrayMat6 = cvCreateMat(ColorImage->height, ColorImage->width, CV_32FC1);
- pGrayMat7 = cvCreateMat(ColorImage->height, ColorImage->width, CV_32FC1);
- BYTE data1; //中間過程變量
- BYTE data2;
- BYTE data3;
- BYTE data4;
- BYTE data5;
- BYTE data6;
- BYTE data7;
- for(int j=0; j<ColorImage->height; j++)
- {
- for(int i=0; i<ColorImage->width; i++)
- {
- data1 = (BYTE)ColorImage->imageData[j*ColorImage->widthStep + i*3]; //B分量
- data2 = (BYTE)ColorImage->imageData[j*ColorImage->widthStep + i*3 + 1]; //G分量
- data3 = (BYTE)ColorImage->imageData[j*ColorImage->widthStep + i*3 + 2]; //R分量
- data4 = max(data1, max(data2, data3)); //最大值
- data5 = (BYTE)((data1 + data2 + data3)/3);
- data6 = (BYTE)(0.072169*data1 + 0.715160*data2 + 0.212671*data3);
- data7 = (BYTE)(0.11*data1 + 0.59*data2 + 0.30*data3);
- cvmSet(pGrayMat1, j, i, data1);
- cvmSet(pGrayMat2, j, i, data2);
- cvmSet(pGrayMat3, j, i, data3);
- cvmSet(pGrayMat4, j, i, data4);
- cvmSet(pGrayMat5, j, i, data5);
- cvmSet(pGrayMat6, j, i, data6);
- cvmSet(pGrayMat7, j, i, data6);
- }
- }
- cvConvert(pGrayMat1, GrayImage1);
- cvConvert(pGrayMat2, GrayImage2);
- cvConvert(pGrayMat3, GrayImage3);
- cvConvert(pGrayMat4, GrayImage4);
- cvConvert(pGrayMat5, GrayImage5);
- cvConvert(pGrayMat6, GrayImage6);
- cvConvert(pGrayMat7, GrayImage7);
- cvNamedWindow( "ColorImage",CV_WINDOW_AUTOSIZE);
- cvNamedWindow( "GrayImage1",CV_WINDOW_AUTOSIZE);
- cvNamedWindow( "GrayImage2",CV_WINDOW_AUTOSIZE);
- cvNamedWindow( "GrayImage3",CV_WINDOW_AUTOSIZE);
- cvNamedWindow( "GrayImage4",CV_WINDOW_AUTOSIZE);
- cvNamedWindow( "GrayImage5",CV_WINDOW_AUTOSIZE);
- cvNamedWindow( "GrayImage6",CV_WINDOW_AUTOSIZE);
- cvNamedWindow( "GrayImage7",CV_WINDOW_AUTOSIZE);
- cvShowImage("ColorImage", ColorImage);
- cvShowImage("GrayImage1", GrayImage1);
- cvShowImage("GrayImage2", GrayImage2);
- cvShowImage("GrayImage3", GrayImage3);
- cvShowImage("GrayImage4", GrayImage4);
- cvShowImage("GrayImage5", GrayImage5);
- cvShowImage("GrayImage6", GrayImage6);
- cvShowImage("GrayImage7", GrayImage7);
- cvWaitKey(0);
- cvDestroyWindow("ColorImage");
- cvDestroyWindow("GrayImage1");
- cvDestroyWindow("GrayImage2");
- cvDestroyWindow("GrayImage3");
- cvDestroyWindow("GrayImage4");
- cvDestroyWindow("GrayImage5");
- cvDestroyWindow("GrayImage6");
- cvDestroyWindow("GrayImage7");
- cvReleaseImage(&ColorImage);
- cvReleaseImage(&GrayImage1);
- cvReleaseImage(&GrayImage2);
- cvReleaseImage(&GrayImage3);
- cvReleaseImage(&GrayImage4);
- cvReleaseImage(&GrayImage5);
- cvReleaseImage(&GrayImage6);
- cvReleaseImage(&GrayImage7);
- cvReleaseMat(&pGrayMat1);
- cvReleaseMat(&pGrayMat2);
- cvReleaseMat(&pGrayMat3);
- cvReleaseMat(&pGrayMat4);
- cvReleaseMat(&pGrayMat5);
- cvReleaseMat(&pGrayMat6);
- cvReleaseMat(&pGrayMat7);
各種不同方法實現灰度化的效果圖對比如下;
圖1 待處理的原始圖像—脫離低級趣味的貓
圖2 分量灰度化—B分量
圖3 分量灰度化—G分量
圖4 分量灰度化—R分量
圖5 最大值灰度化
圖6 均值灰度化
圖7 OpenCV權值係數灰度化
圖8 網絡常用權值灰度化
4、思考
上文的代碼實現中,以最後一種方法爲例,用到了如下代碼:
Gray= (0.11* Blue + 0.59* Green + 0.30* Red);
實際計算機處理時,這種方法已經很快了,但實際上還存在可以優化的餘地。以上代碼所採用的是浮點運算。而在圖像處理中,速度就是生命,實時性往往是很重要的指標,這就要求我們在實現算法時必須考慮到代碼的效率問題。所以有一個原則:在圖像處理中,能不用浮點運算,就最好不要用!
因此,上述代碼可以等效的優化爲:
Gray = (30 * Red + 59 *Green + 11 * Blue) / 100;
這樣一改,可以有效避免浮點運算,因此可以提高代碼的效率
對這行代碼還可以繼續改進爲如下:
Gray= HiByte(77 * Red + 151 * Green + 28 * Blue);
其中77,151,28分別除以256,即爲上文的三個係數。
同樣的,還可以實現爲:
Gray= (77 * Red + 151 * Green + 28 * Blue) shr 8;
這種方法實現了移位運算,避免了除法,效率上又有所提高。
關於具體的代碼效率問題,可以參考博文《由圖像的灰度化看基本圖像處理》。
歡迎登陸我的個人主頁,hello2019,查看原文:http://richardliu.cn/