直方圖均衡化(Histogram Equalization) 又稱直方圖平坦化,實質上是對圖像進行非線性拉伸,重新分配圖像象元值,使一定灰度范圍內象元值的數量大致相等。這樣,原來直方圖中間的峰頂部分對比度得到增強,而兩側的谷底部分對比度降低,輸出圖像的直方圖是一個較平的分段直方圖:如果輸出數據分段值較小的話,會產生粗略分類的視覺效果。
直方圖是表示數字圖像中每一灰度出現頻率的統計關系。直方圖能給出圖像灰度范圍、每個灰度的頻度和灰度的分布、整幅圖像的平均明暗和對比度等概貌性描述。灰度直方圖是灰度級的函數, 反映的是圖像中具有該灰度級像素的個數, 其橫坐標是灰度級r, 縱坐標是該灰度級出現的頻率( 即像素的個數) pr( r) , 整個坐標系描述的是圖像灰度級的分布情況, 由此可以看出圖像的灰度分布特性, 即若大部分像素集中在低灰度區域, 圖像呈現暗的特性; 若像素集中在高灰度區域, 圖像呈現亮的特性。
圖1所示就是直方圖均衡化, 即將隨機分布的圖像直方圖修改成均勻分布的直方圖。基本思想是對原始圖像的像素灰度做某種映射變換, 使變換后圖像灰度的概率密度呈均勻分布。這就意味着圖像灰度的動態范圍得到了增加, 提高了圖像的對比度。
圖1 直方圖均衡化
通過這種技術可以清晰地在直方圖上看到圖像亮度的分布情況, 並可按照需要對圖像亮度調整。另外,這種方法是可逆的, 如果已知均衡化函數, 就可以恢復原始直方圖。
設變量r 代表圖像中像素灰度級。對灰度級進行歸一化處理, 則0≤r≤1, 其中r= 0表示黑, r= 1表示白。對於一幅給定的圖像來說, 每個像素值在[ 0,1] 的灰度級是隨機的。用概率密度函數來表示圖像灰度級的分布。
為了有利於數字圖像處理, 引入離散形式。在離散形式下, 用 代表離散灰度級, 用 代表 , 並且下式成立:
其中, 0≤≤1, k=0, 1, 2, …, n-1。式中 為圖像中出現這種灰度的像素數, n是圖像中的像素總數, 而就是概率論中的頻數。圖像進行直方圖均衡化的函數表達式為: 。
參考:http://www.cnblogs.com/hustlx/p/5245461.html
算法實現(很多在opencv下實現的)
#define HDIM 256 #define SRC 0 #define DST 1 int main(int argc, char** argv) { IplImage *src = 0, *dst = 0; int n[] = {HDIM,HDIM,HDIM}; int r[256] = {0}, g[256] = {0}, b[256] = {0}; if(argc!=2 || (src = cvLoadImage(argv[1],3))== NULL) return -1; cvNamedWindow("source",1); cvNamedWindow("result",1); int width = src->width; int height = src->height; int sum = width * height; //圖像中的像素點綜合 int i,j; //分別統計直方圖的RGB分布 for(i=0; i<height; i++) for(j=0; j<width; j++) { b[((uchar*)(src->imageData+i*src->width))[j*src->nChannels+0]]++; g[((uchar*)(src->imageData+i*src->width))[j*src->nChannels+1]]++; r[((uchar*)(src->imageData+i*src->width))[j*src->nChannels+2]]++; } ////構建直方圖的累計分布方程,用於對直方圖進行均衡化 double val[3] = {0}; for(i=0; i<HDIM; i++) { val[0] += b[i]; val[1] += g[i]; val[2] += r[i]; b[i] = val[0]*255/sum; g[i] = val[1]*255/sum; r[i] = val[2]*255/sum; } dst = cvCreateImage(cvSize(width,height),8,3); //歸一化直方圖 for(i=0; i<height; i++) for(j=0; j<width; j++) { ((uchar*)(dst->imageData+i*dst->widthStep))[j*dst->nChannels+0]=b[((uchar*)(src->imageData+i*src->widthStep))[j*src->nChannels+0]]; ((uchar*)(dst->imageData+i*dst->widthStep))[j*dst->nChannels+1]=g[((uchar*)(src->imageData+i*src->widthStep))[j*src->nChannels+1]]; ((uchar*)(dst->imageData+i*dst->widthStep))[j*dst->nChannels+2]=r[((uchar*)(src->imageData+i*src->widthStep))[j*src->nChannels+2]]; } cvShowImage("source",src); cvShowImage("result",dst); cvSaveImage("out.jpg",dst); cvWaitKey(0); cvDestroyWindow("source"); cvDestroyWindow("result"); cvReleaseImage(&src); cvReleaseImage(&dst); cvReleaseHist(&hist); return 0; }
后面再補充。。。
#include <cv.h> #include <highgui.h> #include <iostream> using namespace std; int main(int argc, char** argv) { //載入圖片 int i=0, j=0, temp=0; IplImage * img = cvLoadImage("D:\\2.jpg", CV_LOAD_IMAGE_GRAYSCALE);//圖片路徑 int height = img->height; int width = img->width; int step = img->widthStep; uchar *data = (uchar*)img->imageData; float size = height*width; //直方圖 unsigned int hist[256] = {0}; for (i=0; i<height; i++) { for (j=0; j<width; j++) { temp = data[i*step+j]; hist[temp]++; } } //歸一化直方圖 float histPDF[256] = {0}; for (i=0; i<255; i++) { histPDF[i]=(float)hist[i]/size; } //累積直方圖 float histCDF[256] = {0}; for (i=0; i<256; i++) { if (0==i) histCDF[i] = histPDF[i]; else histCDF[i] = histCDF[i-1] + histPDF[i]; } //直方圖均衡化,映射 int histEQU[256] = {0}; for (i=0; i<256; i++) { histEQU[i] = (int)(255.0 * histCDF[i] + 0.5); } for (i=0; i<height; i++) { for (j=0; j<width; j++) { temp = data[i*step+j]; data[i*step+j] = histEQU[temp]; } } cvNamedWindow("demo", CV_WINDOW_AUTOSIZE); cvShowImage("demo", img); cvWaitKey(0); cvDestroyWindow("demo"); cvReleaseImage(&img); return 0; }