OpenCV:实现灰度直方图和单通道直方图拉伸
?????? 原文鏈接:http://blog.csdn.net/xiaowei_cqu/article/details/7600666
???? ? 本文略有修改,如有疑問或者版權問題,請移步原作者或者告知本人。
直接貼代碼:
//計算直方圖cv::MatND colorWish::getHist(const cv::Mat &image, int histSize){cv::Mat im;if (image.channels() == 3)cv::cvtColor(image, im, CV_RGB2GRAY, 0);else im = image;float r[2]; r[0] = 0; r[1] = 255;const float *ranges[1];ranges[0] = r;cv::MatND hist;//float range[] = { 0,255 }; //灰度級的范圍 //float* ranges[] = { range };const int channels[1] = { 0 };cv::calcHist(&im, 1, channels, cv::Mat(), hist, 1, &histSize, ranges);return hist;}//直方圖拉伸cv::Mat colorWish::stretch(const cv::Mat &image, int minValue /* = 0 */){int histSize = 256;const cv::Mat hist = getHist(image, histSize);int mini = 0;for (; mini < histSize; ++mini) {if (hist.at<float>(mini) > minValue)break;}int maxi = histSize - 1;for (; maxi >= 0; --maxi) {if (hist.at<float>(maxi) > minValue)break;}cv::Mat lookup(1, 256, CV_8U);for (int i = 0; i < histSize; ++i) {if (i < mini)lookup.at<uchar>(i) = 0;else if (i > maxi)lookup.at<uchar>(i) = 255;elselookup.at<uchar>(i) = static_cast<uchar>((i - mini) * 255 / (maxi - mini) + 0.5);}cv::Mat result;cv::LUT(image, lookup, result);return result;}?
?????? 灰度直方圖是數字圖像中最簡單且有用的工具,這一篇主要總結OpenCV中直方圖CvHistogram的結構和應用。
灰度直方圖的定義
灰度直方圖是灰度級的函數,描述圖像中該灰度級的像素個數(或該灰度級像素出現(xiàn)的頻率):其橫坐標是灰度級,縱坐標表示圖像中該灰度級出現(xiàn)的個數(頻率)。
一維直方圖的結構表示為
???????
高維直方圖可以理解為圖像在每個維度上灰度級分布的直方圖。常見的是二維直方圖。如紅-藍直方圖的兩個分量分別表示紅光圖像的灰度值和藍光圖像灰度值的函數。其圖像坐標(Dr,Db)處對應在紅光圖像中具有灰度級Dr同時在藍光圖像中具有灰度級Db的像素個數。這是基于多光譜——每個像素有多個變量——的數字圖像,二維中對應每個像素統(tǒng)計個變量。
?
OpenCV中的直方圖CvHistogram
注意我們在上面理解直方圖的意義時更多把他想象成一幅“圖”,繼而理解圖中橫坐標,縱坐標的意義。而在OpenCV中,應該更多把直方圖看做“數據結構”來理解。
OpenCV中用CvHistogram表示多維直方圖(CvHistogram):
typedef struct CvHistogram { int type; CvArr* bins; //存放每個灰度級數目的數組指針 float thresh[CV_MAX_DIM][2]; //均勻直方圖 float** thresh2; //非均勻直方圖 CvMatND mat; //直方圖數組的內部數據結構 } CvHistogram;
這個結構看起來簡單(比IplImage*元素少多了。。。)其實并不太好理解。
第一個成員type用來指定第二個成員bins的類型。OpenCv中常見到CvArr*的接口,可以用以指定諸如CvMat、CvMatND、IplImage的類型,其實CvArr*的是一個指向void的指針。在函數內部有時需要得到確切的指向類型,這就需要type來指定。
thresh用來指定統(tǒng)計直方圖分布的上下界。比如[0 255]表示用來統(tǒng)計圖像中像素分別在灰度級[0 255]區(qū)間的分布情況,CV_MAX_DIM對應直方圖的維數,假如設定二維紅-藍直方圖的thresh為[0 255;100 200],就是分別統(tǒng)計紅色圖像灰度級在[0 255]以及藍色圖像在灰度級[100 200]的分布情況。
thresh用以指定均勻直方圖的分布,我們按每個像素理解自然是“均勻分布”,其實也可以統(tǒng)計像素在幾個區(qū)間的分布。如果統(tǒng)計像素在2個區(qū)間的分布,則對應[0 255]的上下界,均勻分布統(tǒng)計的區(qū)間即[0 127] [127 255]分布的概率,這也是為什么thresh第二個維數默認為2——會自動均分上下界;而thresh2指定非均勻的分布,這就需要指定每個區(qū)間的上下界,如果要統(tǒng)計直方圖在區(qū)間(0,10,100,255)的分布,那需要指定thresh2的一個維度為[0 10 100 255],所以用float**形式表示。
mat簡單說就是存儲了直方圖的信息,即我們統(tǒng)計的直方圖分布概率。
?
創(chuàng)建直方圖 cvCreateHist()
OpenCV中用cvCreateHist()創(chuàng)建一個直方圖:
CvHistogram* cvCreateHist( int dims, //直方圖維數 int* sizes,//直翻圖維數尺寸 int type, //直方圖的表示格式 float** ranges=NULL, //圖中方塊范圍的數組 int uniform=1 //歸一化標識 );
size數組的長度為dims,每個數表示分配給對應維數的bin的個數。如dims=3,則size中用[s1,s2,s3]分別指定每維bin的個數。
type有兩種:CV_HIST_ARRAY 意味著直方圖數據表示為多維密集數組 CvMatND; CV_HIST_TREE 意味著直方圖數據表示為多維稀疏數組 CvSparseMat。
ranges就是那個復雜的不好理解的thresh的范圍,他的內容取決于uniform的值。uniform為0是均勻的,非0時不均勻。
計算圖像直方圖的函數為CalcHist():
void cvCalcHist( IplImage** image, //輸入圖像(也可用CvMat**) CvHistogram* hist, //直方圖指針 int accumulate=0, //累計標識。如果設置,則直方圖在開始時不被清零。 const CvArr* mask=NULL //操作 mask, 確定輸入圖像的哪個象素被計數 );
要注意的是這個函數用來計算一張(或多張)單通道圖像的直方圖,如果要計算多通道,則用這個函數分別計算圖像每個單通道。
?
實踐:一維直方圖
下面實踐一下用OpenCV生成圖像的一維直方圖
int main( ) { IplImage * src= cvLoadImage("baboon.jpg"); IplImage* gray_plane = cvCreateImage(cvGetSize(src),8,1); cvCvtColor(src,gray_plane,CV_BGR2GRAY); int hist_size = 256; //直方圖尺寸 int hist_height = 256; float range[] = {0,255}; //灰度級的范圍 float* ranges[]={range}; //創(chuàng)建一維直方圖,統(tǒng)計圖像在[0 255]像素的均勻分布 CvHistogram* gray_hist = cvCreateHist(1,&hist_size,CV_HIST_ARRAY,ranges,1); //計算灰度圖像的一維直方圖 cvCalcHist(&gray_plane,gray_hist,0,0); //歸一化直方圖 cvNormalizeHist(gray_hist,1.0); int scale = 2; //創(chuàng)建一張一維直方圖的“圖”,橫坐標為灰度級,縱坐標為像素個數(*scale) IplImage* hist_image = cvCreateImage(cvSize(hist_size*scale,hist_height),8,3); cvZero(hist_image); //統(tǒng)計直方圖中的最大直方塊 float max_value = 0; cvGetMinMaxHistValue(gray_hist, 0,&max_value,0,0); //分別將每個直方塊的值繪制到圖中 for(int i=0;i<hist_size;i++) { float bin_val = cvQueryHistValue_1D(gray_hist,i); //像素i的概率 int intensity = cvRound(bin_val*hist_height/max_value); //要繪制的高度 cvRectangle(hist_image, cvPoint(i*scale,hist_height-1), cvPoint((i+1)*scale - 1, hist_height - intensity), CV_RGB(255,255,255)); } cvNamedWindow( "GraySource", 1 ); cvShowImage("GraySource",gray_plane); cvNamedWindow( "H-S Histogram", 1 ); cvShowImage( "H-S Histogram", hist_image ); cvWaitKey(0); }
試驗結果:
對應的,我們可以用一樣的思路統(tǒng)計每個通道的直方圖,并繪制圖像每個通道像素的分布:
?
實踐:二維直方圖
我們也可以結合OpenCV的例子生成二維直方圖:
IplImage* r_plane = cvCreateImage( cvGetSize(src), 8, 1 ); IplImage* g_plane = cvCreateImage( cvGetSize(src), 8, 1 ); IplImage* b_plane = cvCreateImage( cvGetSize(src), 8, 1 ); IplImage* planes[] = { r_plane, g_plane }; //將HSV圖像分離到不同的通道中 cvCvtPixToPlane( src, b_plane, g_plane, r_plane, 0 ); // 生成二維直方圖數據結構 int r_bins =256, b_bins = 256; CvHistogram* hist; { int hist_size[] = { r_bins, b_bins }; float r_ranges[] = { 0, 255 }; // hue is [0,180] float b_ranges[] = { 0, 255 }; float* ranges[] = { r_ranges,b_ranges }; hist = cvCreateHist( 2, hist_size, CV_HIST_ARRAY, ranges, 1); } //計算一張或多張單通道圖像image(s) 的直方圖 cvCalcHist( planes, hist, 0, 0 );
剛才的圖我們是對應每個橫坐標繪制縱坐標的直方塊,二維的圖需要繪制每個點:
最終生成二維直方圖:
?
直方圖的應用以后再討論。
轉載請注明出處:http://blog.csdn.net/xiaowei_cqu/article/details/7600666
實驗代碼下載:http://download.csdn.net/detail/xiaowei_cqu/4328881
Mat格式的參考這里:http://blog.csdn.net/xiaowei_cqu/article/details/8833799
總結
以上是生活随笔為你收集整理的OpenCV:实现灰度直方图和单通道直方图拉伸的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Visual Studio 17.7 P
- 下一篇: 旺影速转如何转换视频格式?旺影速转转换视