歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> 圖解直方圖均衡化及其Python實現

圖解直方圖均衡化及其Python實現

日期:2017/3/1 9:11:08   编辑:Linux編程

在理解直方圖均衡化的過程中,參考了一些書籍和博客,讓人困惑的是,筆者對於直方圖的理解還是停留在表面,並沒有深入理解其內涵。因此,本文擬結合圖片對直方圖的概念進行闡述,並給出其Python實現,最後對她背後所蘊含的一些科學思維,談談自己的一些看法。

什麼是直方圖?

對於一副灰度圖像I,她的每一個像素點I(x,y)都有一個灰度值,一般情況下可能的灰度取值有2^8=256個(0,1,...,255)。如果我們統計出灰度值r在I中出現的次數n,並對其進行歸一化(n/N,N是所有灰度值出現次數的總和),這樣我們就可以得到像素r在I中出現的概率p(r)。如果對每一個可能的灰度取值r都做同樣的處理,我們可以得到如圖1左側所示的概率分布曲線,該曲線就是我們常說的直方圖。

圖1 直方圖均衡化目標

什麼是直方圖均衡化?

通常情況下一副圖像I的直方圖如圖1左側所示,每一個灰度值r出現的概率不是相等的,這樣會導致圖像的一些細節信息不夠突出,而直方圖均衡化就是對灰度值r進行如下變換s=T(r),使得變換後的灰度分布如圖1右側所示(也就是說,每一個灰度值出現的概率是想同的),這樣能夠發現一些原先肉眼很難發現的細節,如圖2所示(讀者可以自己體會下)。說到這裡,一般也就結束了,但是我們真的理解了嗎?如何更好的理解呢?下面簡要介紹一下筆者的理解方式。

假設我們有四個灰度級a,b,c,d,做個類比的話可以將她們理解為四個描述情感的詞喜、怒、哀、樂。一般情況下我們得到的直方圖可認為是p(a)=0.5,p(b)=0.5,p(c)=0,p(d)=0,而均衡化後的直方圖是p(a)=0.25,p(b)=0.25,p(c)=0.25,p(d)=0.25. 如果我們以均衡化前的詞描述人的情感則只有喜、怒,而均衡化後的詞則有喜、怒、哀、樂。試問,哪種情形能夠描述人更細微的情感變化呢?這也是筆者認為直方圖均衡化之後能夠描述更多圖像細節的原因。

更進一步,如果一般情況下我們得到的直方圖是p(a)=0.4,p(b)=0.4,p(c)=0.1,p(d)=0.1,那這按我們的理解意味什麼呢?意味著大部分情況用喜、怒描述人的情感,而哀、樂幾乎不用,當然了,如果能夠自由運用喜、怒、哀、樂來描述人的情感是最好的哦。

圖2 直方圖均衡化示意圖(左圖變換前,右圖變換後)

直方圖均衡化的數學原理

筆者曾經是個數學控,總是認為凡事都應該從數學的基本原理出發,然而運用數學推理得出我們期望的結論。然而此刻,覺得自己的想法從根本上是錯誤的。下面以直方圖均衡化數學原理為例進行說明。

我們直方圖均衡化的目標如圖1所示,那麼怎麼做到呢?這中間的約束條件又是什麼呢(世上沒有絕對的自由)?

可以設r原始圖像灰度級,s均衡化後圖像灰度級,不失一般性可以假設r的取值范圍為[0,1]。那麼什麼是必須滿足的約束條件呢?均衡化前後灰度級的意義不能變,也就是說變換前灰度級的含義是由黑到白,那麼變換後也應該如此。此外,最好變換前後灰度級的取值范圍一致。為使變換後的灰度仍保持從黑到白的單一變化順序,且變換范圍與原先一致,以避免整體變亮或變暗。必須規定:

(1)變換函數T(r)在r屬於[0,1]范圍內是單值、單調增函數;

(2)對於r屬於[0,1],s=T(r)也屬於[0,1]

圖3 直方圖均衡化原理示意圖

這兩條規定是滿足我們均衡化要求的數學化描述(如果要應用數學的話,就應該將我們要求、目標等轉化為數學語言進行描述)。

直方圖均衡就是通過灰度變換函數s=T(r)將原圖像直方圖p (r)改變為均勻分布的直方圖p(s)。由圖3可知,在滿足兩條規定的前提下,因為s=T(r)(也就是說,r確定,s確定),結合概率論可知p (r)dr=p(s)ds又因為直方圖均衡化後,有:p(s) = 1, s屬於[0,1],因此,ds=p(r)dr。兩邊取積分有。

這樣我們就找到了s、r之間的映射關系。

直方圖均衡化Python實現

由s、r之間的映射關系

可知,對於一副輸入灰度圖像I,我們首先計算其p(r),然後計算每一個灰度級r對應的

def histeq(img,nbr_bins=256):
    """ Histogram equalization of a grayscale image. """
    
# 獲取直方圖p(r)
imhist, bins = histogram(img.flatten(), nbr_bins, normed = True)
# 獲取T(r)
cdf = imhist.cumsum() # cumulative distribution function cdf = 255 * cdf /cdf[-1] # 獲取s,並用s替換原始圖像對應的灰度值 result = interp(img.flatten(),bins[:-1],cdf) return result.reshape(img.shape),cdf

一些思考

筆者以前有這樣一種誤解:以為所有的理論都可以從基本的數學原理推導得出,於是在碰到新問題的時候,總是試圖搞清楚各個數學公式的來源及其物理含義,而忽略了這些數學運作背後的目的是什麼!這樣的一個缺點就是,今天能夠理解的理論,過了幾天就不理解了。

現在筆者的一些體會是,如果按照以下思路理解問題,能夠起到事半功倍的效果(有點誇張的成分在裡面):首先搞清楚問題的來源、其次知曉解決問題的目的是什麼、然後將問題數學化描述(時刻注意約束條件)、最後求解該數學問題。

總結:通常情況下,如果我們能夠將前三個步驟走完,最後的求解問題通過借助於現有軟件、算法,應該是水到渠成的事。(本人非數學專業,對怎麼求解一個數學問題,通常只關心輸入、輸出、以及約束條件)

Copyright © Linux教程網 All Rights Reserved