直方圖均衡化的原理及C++實現
阿新 • • 發佈:2019-01-01
直方圖均衡化的作用是影象增強。
有兩個問題比較難懂,一是為什麼要選用累積分佈函式,二是為什麼使用累積分佈函式處理後像素值會均勻分佈。
第一個問題。均衡化過程中,必須要保證兩個條件:①畫素無論怎麼對映,一定要保證原來的大小關係不變,較亮的區域,依舊是較亮的,較暗依舊暗,只是對比度增大,絕對不能明暗顛倒;②如果是八點陣圖像,那麼畫素對映函式的值域應在0和255之間的,不能越界。綜合以上兩個條件,累積分佈函式是個好的選擇,因為累積分佈函式是單調增函式(控制大小關係),並且值域是0到1(控制越界問題),所以直方圖均衡化中使用的是累積分佈函式。
第二個問題。累積分佈函式具有一些好的性質,那麼如何運用累積分佈函式使得直方圖均衡化?比較概率分佈函式和累積分佈函式,前者的二維影象是參差不齊的,後者是單調遞增的。直方圖均衡化過程中,對映方法是
其中,n是影象中畫素的總和,是當前灰度級的畫素個數,L是影象中可能的灰度級總數。
來看看通過上述公式怎樣實現的拉伸。假設有如下影象:
得影象的統計資訊如下圖所示,並根據統計資訊完成灰度值對映:
對映後的影象如下所示:
程式碼實現
灰度密度統計:
///***************************************************************/ /*函式名稱:ZhiFangTu(float *tongji) /*函式型別:void /*變數說明:tongji 灰度分佈密度統計 /*功能:對影象進行灰度直方圖統計。 /***************************************************************/ void HuiDuBianHuanDib::ZhiFangTu(float *tongji) { // 迴圈變數 int i; int j; // 灰度計數 int huidu[256]; int wide,height; //原圖長、寬 wide=this->GetWidth (); height=this->GetHeight (); // 變數初始化 memset(huidu,0,sizeof(huidu)); LPBYTE temp1=new BYTE[wide*height]; //新影象緩衝區 //拷貝原影象到快取影象 memcpy(temp1,m_pData,wide*height ); // 對各畫素進行灰度統計 for (j = 0; j < height; j ++) { for (i = 0; i <wide; i ++) { unsigned char temp = temp1[wide* j + i] ; // 灰度統計計數 huidu[temp]++; } } // 計算灰度分佈密度 for(i=0;i<256;i++) tongji[i] = huidu[i] / (height * wide *1.0f); }
均衡化:
///***************************************************************/ /*函式名稱:Fenbujunhenghua() /*函式型別:void /*變數說明:無 /*功能:對影象進行灰度分佈均衡化處理。 /***************************************************************/ void HuiDuBianHuanDib::Fenbujunhenghua( ) { // 迴圈變數 LONG i; LONG j; //影象的寬和高 LONG wide; LONG height; // 灰度分佈密度 float midu[256]; // 中間變數 float temp[256]; int nDstGray[256]; // 初始化 memset(temp, 0, sizeof(temp)); // 指向DIB象素指標 LPBYTE p_data; // 找到DIB影象象素起始位置 p_data = this->GetData(); wide=this->GetWidth (); // DIB的高度 height = GetHeight(); // 獲取影象的灰度分佈密度 ZhiFangTu(midu); // 進行均衡化處理 for(i = 0; i < 256; i++) { if(i == 0) { temp[0] = midu[0]; } else { temp[i] = temp[i-1] + midu[i]; } nDstGray[i] = (int)(255.0f * temp[i] + 0.5f); } // 對各畫素進行灰度轉換 for (j = 0; j < height; j ++) { for (i = 0; i < wide; i ++) { // 將轉換後的灰度分佈寫入DIB影象,nDstGray[temp]為原灰度級變換後的灰度值 unsigned char temp = *((unsigned char *)p_data + wide * j + i); *((unsigned char *)p_data + wide * j + i) = nDstGray[temp]; } } }