【学习图像处理】之实验二——灰度图像直方图规定化
灰度圖像直方圖規(guī)定化
- 圖像增強(qiáng)
- 一、實驗內(nèi)容
- 二、灰度直方圖
- 1、什么是灰度直方圖?
- 2、直方圖均衡化
- 3、直方圖規(guī)定化
- 三、代碼實現(xiàn)與分析
- 0、輔助功能實現(xiàn)
- 1、繪制原圖像直方圖SH
- 2、繪制均衡直方圖SQH ,給出均衡圖象QI
- 3、繪出目標(biāo)均衡直方圖EQH
- 4、繪制規(guī)定化變換函數(shù)T,給出最終增強(qiáng)圖象DI及其直方圖DH
- 結(jié)語
圖像增強(qiáng)
上一回我們通過進(jìn)行圖像反白、調(diào)整調(diào)色板取值和彩圖變灰圖的實驗對bmp圖像的數(shù)據(jù)格式有了比較熟練的了解。其實一張圖片上往往只有很少一部分信息是我們真正需要的,而這些信息卻埋沒于茫茫的像素之中,利用起來很不方便,因此我們就需要對圖像進(jìn)行增強(qiáng)。
增強(qiáng)什么?當(dāng)然是依據(jù)需求定向的加強(qiáng)指定內(nèi)容。今天的主題就是對灰度圖像進(jìn)行直方圖均衡化(Histogram Equalization)與直方圖規(guī)定化(Histogram Specifications)處理。
一、實驗內(nèi)容
二、灰度直方圖
1、什么是灰度直方圖?
對于灰度圖而言,畫面是由很多個不同灰度值的像素組成的,圖像中灰度的分布情況就成為了一個很重要的特征信息,因為它直接決定了整張圖片什么信息最為突出,而另一些則不太明顯。
灰度直方圖,即是對圖像中每個灰度級的像素數(shù)做了統(tǒng)計,繪制成的以灰度級為橫坐標(biāo),像素個數(shù)(頻率)為縱坐標(biāo)的圖像,它能夠直觀的體現(xiàn)灰度圖像中某種灰度出現(xiàn)的概率,表達(dá)式為: Pr(rk)=nknPr(rk)=\frac{nk}{n}Pr(rk)=nnk? ,其中rkrkrk為灰階,nknknk表示灰度級為rkrkrk的像元數(shù)目。
當(dāng)然直方圖也有一個很明顯的缺點,就是它丟失了像素所在位置的信息。
2、直方圖均衡化
一般一張自然的灰度圖都會集中在相對較窄的一段灰階內(nèi),這樣就會導(dǎo)致細(xì)節(jié)丟失。如果我們通過某種手段,把灰度區(qū)間拉大或者讓灰度盡可能均勻分布,這樣做就可以增大圖像對比度,從而把細(xì)節(jié)信息顯示出來。
直方圖均衡化就是一種達(dá)成上述目的的手段。均衡化處理后,灰度范圍變大,對比度變大,清晰度變大,能夠有效增強(qiáng)圖像。其具體的推導(dǎo)過程涉及概率論中知識,感興趣可以看這位作者撰寫的直方圖均衡化的數(shù)學(xué)原理,而它的作用解釋可以見直方圖均衡化作用。
對于我們寫代碼而言,只需要知道最終的變換公式即可:
y=f(x)=(L?1)∑0xih(xi)w?hy=f(x)=(L-1)\sum_{0}^{x_i} \frac{h(x_i)}{w*h}y=f(x)=(L?1)∑0xi??w?hh(xi?)?
3、直方圖規(guī)定化
上面講到了直方圖均衡化,現(xiàn)在有一個問題:如果兩張圖片s和u,他們都可以均衡化到t,那是否意味著s可以通過某種方法轉(zhuǎn)換成u呢?答案是肯定的,我們用幾個公式來表示s、u、t間的關(guān)系:
t=T(sk)=∑i=0kps(si)t=T(s_k)=\sum_{i=0}^{k} p_s(s_i)t=T(sk?)=∑i=0k?ps?(si?) (1) k=0,1,...,M?1k=0,1,...,M-1k=0,1,...,M?1
t=Tu(uj)=∑j=0lpu(uj)t=T_u(u_j)=\sum_{j=0}^{l} p_u(u_j)t=Tu?(uj?)=∑j=0l?pu?(uj?) (2) l=0,1,...,N?1l=0,1,...,N-1l=0,1,...,N?1
由此可見,我們想要通過均衡化的結(jié)果逆求出u是可行的。我們只需要找到s到u的映射關(guān)系T,而這個關(guān)系應(yīng)該是:
∣∑i=0kps(si)?∑i=0lpu(uj)∣|\sum_{i=0}^{k} p_s(s_i)-\sum_{i=0}^{l} p_u(u_j)|∣∑i=0k?ps?(si?)?∑i=0l?pu?(uj?)∣
三、代碼實現(xiàn)與分析
這次實驗中我們用下面這張圖作為原圖像:
0、輔助功能實現(xiàn)
如果你仔細(xì)分析一下實驗的每一步,會發(fā)現(xiàn)我們需要多次的存儲用以繪制直方圖的數(shù)據(jù),而且這個數(shù)據(jù)往往是保存在一個數(shù)組之中,因此我們可以專門寫一個函數(shù)將數(shù)組保存到txt文件中去。
template<typename T> bool array2txt(T &H,const char* cFilename) //將一個數(shù)組輸出到以cFilename命名的txt文件中 { ofstream outfile;outfile.open(cFilename, ios::out);if (!outfile.is_open()){cout << "Open file failure" << endl;return false;}for (int i = 0; i < (sizeof(H)/sizeof(H[0])); i++) //遍歷數(shù)組寫入txt{outfile << H[i] << "\t" ;}return true; }可以看到,這里我為了能夠反復(fù)利用使用了模版函數(shù)和c++中的文件流輸出語句,以此來避免文件輸出時該用%d還是%f的問題。不過實際上我們大部分?jǐn)?shù)據(jù)都是float類型的數(shù)組,所以模版函數(shù)是可選的。
1、繪制原圖像直方圖SH
正式開始實驗,在本次實驗中的直方圖我都以“灰階-概率”的形式輸出,如果你想輸出“灰階-頻率”的直方圖,那么不要除以總數(shù)即可。
float* outHistogramData(BMPFILE &src,const char*cFilename)//將繪制“灰階-概率”直方圖的數(shù)據(jù)保存到txt文件中 {float H[256] = { 0 }; //8bit的灰度圖,256灰階int i, j;float *result = (float*)malloc(256 * sizeof(float));int total = src.imageh * src.imagew;for (i = 0; i < src.imageh; i++)for (j = 0; j < src.imagew; j++){H[src.pDataAt(i)[j]] += 1.0; //對每個灰階進(jìn)行計數(shù)}for (i = 0; i < 256; i++){H[i] /= (float)total; //計算圖像概率p(Sk)result[i] = H[i];}if (!array2txt(H,cFilename)){printf("outHistogramData error!\n");exit(0);}return result; }這里一定要注意,如果你想要返回函數(shù)中新建的一個數(shù)組,那么一定要用malloc對它進(jìn)行內(nèi)存分配,否則返回后的指針將是野指針。同時,由于我array2txt函數(shù)的定義問題,它不可以接受一個指針表示的數(shù)組,這個可以由你自行修改。我的解決辦法是單獨創(chuàng)建一個定長數(shù)組H[256],讓它傳入array2txt函數(shù),另分配一個指針result返回結(jié)果。
我們得到的txt文本中數(shù)據(jù)大致長這個樣子(后面的過程我就不貼數(shù)據(jù)的圖了)
把這些數(shù)據(jù)導(dǎo)入到Excel表格中即可做出我們要的直方圖SH:
2、繪制均衡直方圖SQH ,給出均衡圖象QI
根據(jù)我們之前的分析,均衡直方圖的求解需要對“灰階-概率”數(shù)據(jù)進(jìn)行累加處理,然后再擴(kuò)展到8bit對應(yīng)的0-255范圍上。這里“灰階-概率”我們可以利用上一步中得到的數(shù)據(jù)(當(dāng)然你也可以再算一遍,少傳一個參數(shù))
float* HistEqualize(BMPFILE &src,BMPFILE &des,float* hist_data, const char*cFilename) //已知灰階-概率矩陣,做直方圖均衡化 {float count_origin[256] = { 0 }, T[256] = { 0 }, count_new[256] = { 0 };//count_origin用來記錄原圖像各灰階個數(shù),count_new是均衡化之后的結(jié)果float *result = (float*)malloc(256 * sizeof(float)); //返回均衡化后的灰階-概率矩陣int i, j;int total = src.imageh * src.imagew;for (i = 0; i < 256; i++){count_origin[i] = hist_data[i] * total; //求原圖中的灰階計數(shù)}for (i = 0; i < 256; i++)for (j = 0; j <= i; j++){T[i] += hist_data[j]; //求得均衡化之后的tkresult[i] = T[i]; //如果需要作圖的那么result=count_new,如果需要均衡化result=T}if(!array2txt(T, "tk_data.txt")){printf("outHistogramData error!\n");exit(0);}for (i = 0; i < 256; i++){T[i] = int((256 - 1)*T[i] + 0.5); //用式tk=int((L-1)*tk+0.5)將tk擴(kuò)展至[0,L-1]范圍,得到灰度級}for (i = 0; i < src.imageh; i++)for (j = 0; j < src.imagew; j++){des.pDataAt(i)[j] = T[src.pDataAt(i)[j]]; //將新的值賦予給輸出圖像}for (i = 0; i < 256; i++){count_new[int(T[i])] += count_origin[i]; //根據(jù)新的灰度級求新的計數(shù)}for (i = 0; i < 256; i++){count_new[i] /= (float)total; //計算新圖像概率p(tk)//result[i] = count_new[i]; //如果需要作圖的那么result=count_new,如果需要均衡化result=T}if(!array2txt(count_new, cFilename)){printf("outHistogramData error!\n");exit(0);}des.SaveBMPFILE("QI.bmp");return result; }對于最后輸出圖像的賦值以及灰階的重新計數(shù)如果有不明白的,可以看看下面這張圖:
我舉一個例子,如果原圖中灰度級為3,那么它在輸出圖像中的灰度級就應(yīng)該是T[3]=6。這就是為什么
得到的均衡直方圖SQH如圖:
均衡圖像QI則是:
3、繪出目標(biāo)均衡直方圖EQH
可能是直接給出直方圖數(shù)據(jù)缺乏挑戰(zhàn)性,老師給了我們一個函數(shù),要求將函數(shù)轉(zhuǎn)換為目標(biāo)直方圖。函數(shù)表達(dá)式為:
y=a(14(x?12)2)y=a(\frac{1}{4}(x-\frac{1}{2})^{2})y=a(41?(x?21?)2),x∈[0,1]x\in[0,1]x∈[0,1]
注意到函數(shù)中含有未知量a,所以我們需要找到方程解出a的值。既然它代表直方圖,那么它就應(yīng)該符合直方圖的性質(zhì)——頻率之和為1。有了這個入手點,我們就可以列方程了:
1=∫01f(x)dx1=\int_{0}^{1} f(x)dx1=∫01?f(x)dx
接下來通過微分的基本思想,求解未知數(shù):
求得a的值無限逼近于6,因此函數(shù)即為y=6(14(x?12)2)y=6(\frac{1}{4}(x-\frac{1}{2})^{2})y=6(41?(x?21?)2)。接下來我們需要把圖像放縮到0-255的區(qū)間上,可以仿照上面的微分思路,將0-1均分為256份,然后分別求概率值即可(注意結(jié)果也要相應(yīng)放縮)。
這樣我們就得到了目標(biāo)直方圖EH的“灰階-概率”數(shù)據(jù),將其傳入2中函數(shù)(這里理論上是無法得出圖像的,但是因為u也是s經(jīng)過某種處理后得到的圖像,因此我們可以將s作為原圖傳入函數(shù)中),即可求得EQH:
4、繪制規(guī)定化變換函數(shù)T,給出最終增強(qiáng)圖象DI及其直方圖DH
在完成步驟2、3的過程中我們已經(jīng)求得了SQH_tk和EQH_tk兩個數(shù)組,即直方圖規(guī)定化所需要的所有數(shù)據(jù)已經(jīng)齊了,接下來我們按照公式去求規(guī)定化需要的映射函數(shù)T。規(guī)定化的主要步驟就是把SQH_tk中的每一個灰度映射到一個EQH_tk中與之差值最小的灰度值上,由于tk是單調(diào)遞增的,因此我們只需要判斷到第一個大于SQH_tk[i]的值即可。得到映射函數(shù)T之后,我們將原圖像中的像素按照映射關(guān)系重新賦值即可得到DI。代碼如下:
int* HistSpecificate(HXLBMPFILE &src, HXLBMPFILE &des, float* hist_data, const char*cFilename) //直方圖規(guī)定化 {int i, j, index=0;float *SQH_tk = HistEqualize(src, des, "SQH.txt"); //獲取原圖像的均衡化tk數(shù)組float *EQH_tk = HistEqualize(src, des, hist_data,"EQH.txt"); //獲取目標(biāo)圖像的均衡化tk數(shù)組int T[256]; //灰階對應(yīng)關(guān)系矩陣int *result = (int*)malloc(256 * sizeof(int));float min, temp;for (i = 0; i < 256; i++){min = fabs(SQH_tk[i] - EQH_tk[0]); //假設(shè)最小值for (j = index; j < 256; j++){temp = fabs(SQH_tk[i] - EQH_tk[j]); //依次做差找到最小值if (temp < min){min = temp;index=j;}if (SQH_tk[i] < EQH_tk[j])break;}T[i] = index; //保存映射結(jié)果result[i] = index;}if(!array2txt(T,"HistSpecificate.txt")){printf("outHistogramData error!\n");exit(0);}for (i = 0; i < src.imageh; i++)for (j = 0; j < src.imagew; j++){des.pDataAt(i)[j] = T[src.pDataAt(i)[j]]; //將新的值賦予給輸出圖像}des.SaveBMPFILE("DI.bmp");outHistogramData(des, cFilename);return result; }最終得到的變換函數(shù)T如圖:
增強(qiáng)圖像DI與其直方圖DH分別是:
結(jié)語
第二次圖像處理的實驗,說實話直方圖均衡化的代碼寫起來很簡單,但是真正的重點在于如何理解直方圖均衡化以及規(guī)定化的意義,因為實際應(yīng)用中我們可以直接從opencv這樣的庫中調(diào)用函數(shù)。況且如果是為了寫代碼而寫,那么完全可以去一些OJ網(wǎng)站刷題,因此對于算法的理解才是學(xué)習(xí)過程中應(yīng)該注意的地方。
本次實驗完整的項目文件與代碼可見我的gitee。
總結(jié)
以上是生活随笔為你收集整理的【学习图像处理】之实验二——灰度图像直方图规定化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 维特比算法
- 下一篇: 11月总结#nobody