用C++(OpenCV)自己實現彩色直方圖均衡化
阿新 • • 發佈:2019-02-09
對於直方圖均衡化,我的理解是一個影象原來的畫素集中在一個灰度範圍裡,我們對這種圖不敏感,因為其灰度值集中,變化不明顯。如果我們把這些畫素點的灰度範圍擴大,影象就會顯得比較清晰。直方圖均衡化就是通過一個對映函式,把原影象素點的灰度值對映為一個新的值,使得影象的畫素點灰度值在全部灰度範圍內比較均勻分佈。
直方圖均衡化主要有四步:
第一步是統計每個灰度值出現的次數。
第二步是計算累積函式。
第三步是根據對映函式,建立查詢表。
第四步是根據查詢表計算新的畫素值。
OpenCV自帶的函式cvEqualizeHist可以實現直方圖均衡化,我參考OpenCV該函式的原始碼,自己寫了一個myEqualizeHist函式來實現。
另外,為了對均衡化的結果進行理性的分析,我還自己寫了一個畫直方圖的函式。
原理是我先統計好每個灰度值的畫素點個數,然後呼叫opencv裡的cvRectangle函式,對於每一個灰度值(0-255)都畫出一個對應高度的小矩形,矩形的高度就是該灰度值對應的畫素點的個數。
程式碼如下:
#include <highgui.h> #include <cv.h> #include <stdio.h> using namespace cv; using namespace std; IplImage* myDrawHistogram(int* hist_cal) { //找出直方圖中最大值,以便進行歸一化 int hist_max = 0; for(int i = 0; i < 256; i++) { if(hist_cal[i] > hist_max) hist_max = hist_cal[i]; } //建立一幅空白的影象,用來顯示直方圖 IplImage* hist_image = cvCreateImage(cvSize(256 * 3, 64 * 3), 8, 1); cvZero(hist_image); for(int i = 0; i < 256; i++) { CvPoint p0 = cvPoint(i * 3, 0); CvPoint p1 = cvPoint((i + 1) * 3, cvRound(((hist_max - hist_cal[i]) * 64) / hist_max * 3)); cvRectangle(hist_image, p0, p1, cvScalar(255, 255, 255), -1, 8, 0); } return hist_image; } int p[256]; int dst_p[256]; void myEqualizeHist(CvArr* srcarr, CvArr* dstarr) { CvMat sstub; CvMat dstub; CvMat *src = cvGetMat(srcarr, &sstub);//convert CvArr to CvMat CvMat *dst = cvGetMat(dstarr, &dstub); CvSize size = cvGetMatSize(src); int x, y; const int hist_size = 256; //int p[hist_size];//p陣列長度為影象的灰度等級(一般為256) fill(p, p + hist_size, 0);//初始化為0 //掃描影象的每一個畫素點,畫素值為k則hist[k]++ for(y = 0; y < size.height; y++) { const uchar* sptr = src->data.ptr + src->step * y; for(x = 0; x < size.width; x++) { p[sptr[x]]++;//相當於[x][y]這個點對應的畫素值++ } } int c[hist_size]; c[0] = p[0]; //累積函式 for(int i = 1; i < hist_size; i++) { c[i] = c[i - 1] + p[i]; } uchar lut[hist_size]; //根據對映函式,建立look up table for(int i = 0; i < hist_size; i++) { int val = cvRound(c[i] * (255.f / (size.width * size.height))); lut[i] = CV_CAST_8U(val);//畫素值i對映之後值為lut[i] } //根據look up table,改變影象畫素值 for(y = 0; y < size.height; y++) { const uchar* sptr = src->data.ptr + src->step * y; uchar* dptr = dst->data.ptr + dst->step * y; for(x = 0; x < size.width; x++) { dptr[x] = lut[sptr[x]]; } } //計算直方圖均衡化之後的影象的畫素分佈 //int dst_p[hist_size]; fill(dst_p, dst_p + hist_size, 0); for(y = 0; y < size.height; y++) { const uchar* dst_sptr = dst->data.ptr + dst->step * y; for(x = 0; x < size.width; x++) { dst_p[dst_sptr[x]]++;//相當於[x][y]這個點對應的畫素值++ } } } int main() { IplImage* image = cvLoadImage("D:\\lena.jpg", 1); cvShowImage("原影象", image); IplImage* redImage = cvCreateImage(cvGetSize(image), image->depth, 1); IplImage* greenImage = cvCreateImage(cvGetSize(image), image->depth, 1); IplImage* blueImage = cvCreateImage(cvGetSize(image), image->depth, 1); cvSplit(image, blueImage, greenImage, redImage, NULL); myEqualizeHist(redImage, redImage); cvShowImage("對影象進行直方均衡化之前的R直方圖", myDrawHistogram(p)); cvShowImage("對影象進行直方均衡化之後的R直方圖", myDrawHistogram(dst_p)); myEqualizeHist(greenImage, greenImage); cvShowImage("對影象進行直方均衡化之前的G直方圖", myDrawHistogram(p)); cvShowImage("對影象進行直方均衡化之後的G直方圖", myDrawHistogram(dst_p)); myEqualizeHist(blueImage, blueImage); cvShowImage("對影象進行直方均衡化之前的B直方圖", myDrawHistogram(p)); cvShowImage("對影象進行直方均衡化之後的B直方圖", myDrawHistogram(dst_p)); cvMerge(blueImage, greenImage, redImage, NULL, image); cvShowImage("自己實現的直方圖均衡化", image); waitKey(0); return 0; }