不调用python函数实现直方图均衡化_直方图均衡化(HE)
前面我們已經講過圖像的直方圖,那圖像的直方圖均衡化又是干嘛的呢?
顧名思義:其實對直方圖進行均衡化,哈哈感覺自己說的就是廢話...
舉個例子:
import cv2 from matplotlib import pyplot as pltimg = cv2.imread("src.jpg", 0)plt.hist(img.ravel(), 256, [0, 256])plt.savefig("histogram.jpg", dpi = 300, bbox_inches = "tight", pad_inches = 0)# dpi : dot per inch # bbox_inches: if 'tight', try to figure out the tight bbox of the figure. # pad_inches: amount of padding[填充] around the figure when bbox_inches is 'tight'.plt.show()圖1 原圖及對應的直方圖觀察圖1:原圖的亮暗對比不明顯,感性上,感覺整幅圖像就是灰蒙蒙的.
通過繪制原圖的灰度直方圖,可以看到,像素基本集中在100~200之間,其它的幾乎沒有,因此可理性判斷,該圖像不能夠很好的突出細節信息.
Q1: 怎么才能讓圖像看起來層次比較分明呢?(包含細節信息)
A1: 需要對灰度值進行相關的處理(各種均衡化算法),使圖像的像素分布盡可能廣泛,改善圖像的對比度.
圖2 像素范圍拉伸1.直方圖均衡化
首先需要明白一個概念:累積分布函數(CDF : Cumulative Distribution Function)
可參考:
0704:概率分布函數、概率密度函數?zhuanlan.zhihu.com假設你明白了,累積分布函數的相關概念.
現在我們開始直接舉例:
下圖是一個8 x 8 的 灰度圖像的灰度值.
圖3 灰度值對灰度值的出現次數進行統計:
表1 灰度值的頻率統計更進一步:計算累積分布函數值,為簡化,累積分布函數值為0的灰度值被省略.
表2 累積分布函數值直方圖均衡化的公式如下:
注:式(1) 中的round()是一個函數a: 為整數,round(a)不變;
a: 為浮點數,round(a)圓整到離它最近的整數
a: 為浮點數,且距離兩個整數一樣近時,圓整到偶數.
比如:a = 1.5, 則round(a) = 2
本例中,
注:L為圖像的灰度級數,圖像為灰度圖,所以位深度為8,故灰度級數共有2^8 = 256 級數.均衡化后的灰度是多少呢?
用灰度78進行實驗:
依次類推:可計算出原圖中每個灰度均衡化后的灰度,如圖4所示:
圖4 均衡化后的灰度注:最小值52變為了0,最大值154變為了255.圖5 原始圖像與均衡化后圖像的對比圖代碼實現:
① 原始圖像的直方圖及累積函數圖像:
from cv2 import cv2 import numpy as np from matplotlib import pyplot as pltimg = cv2.imread("src.jpg", 0)hist, bins = np.histogram(img.ravel(), 256, [0, 256])cdf = np.cumsum(hist) #計算累積函數值 cdf_normalized = cdf * max(hist) / max(cdf)plt.plot(cdf_normalized, color = "r") plt.hist(img.ravel(), 256, [0, 256])plt.legend(("cdf", "histogram"), loc = "upper left")plt.show()圖6 原圖及對應的直方圖與累積分布圖我們可以看出來直方圖大部分在灰度值較高的部分,而且分布很集中。而 我們希望直方圖的分布比較分散,能夠涵蓋整個 x 軸。所以,我們就需要一個 變換函數幫助我們把現在的直方圖映射到一個廣泛分布的直方圖中。這就是直方圖均衡化要做的事情。
② 原始圖像均衡化后的直方圖與累積函數分布圖
為便于與直方圖均衡化算法原理相結合,我們分為以下幾步來實驗.
第一步:看一下原圖像直方圖縱坐標值
我們用程序跑一下,顯示原圖像直方圖縱坐標值(也就是上面代碼中hist數組里的值),如下圖7所示:
圖7 原圖像的hist數組hist數組:序號表示的是灰度,數值表示灰度的個數.同樣:我們可以看原圖像累積函數cdf的值,如下圖8所示:
圖8 原圖像的cdf函數值Q1:cdf中含有大量的0值,我們上述的算法是要忽略0,那怎么辦呢?
A1:使用numpy
# 構建 Numpy 掩膜數組,cdf 為原數組,當數組元素為 0 時,掩蓋(計算時被忽略). cdf_m = np.ma.masked_equal(cdf,0)使用這個函數,效果如何呢?如圖9所示:
圖9 Numpy掩膜數組注:計算時,0會被忽略.
第二步:算法公式
其實比較容易實現,如下:
cdf_m = (cdf_m - cdf_m.min())*255/(cdf_m.max()-cdf_m.min())可以得到映射完后的灰度值,如圖10所示:
圖10 映射后的灰度值第三步:對掩蓋的元素重新賦值0
圖10所顯示映射后的灰度值,需對被掩蓋的元素重新賦值0
Q1:該怎么做?
# 對被掩蓋的元素賦值,這里賦值為 0 cdf = np.ma.filled(cdf_m, 0).astype('uint8') # 數據類型為:uint8使用這個函數后,效果如圖11所示:
圖11 賦值0現在就獲得了一個表(里面的值為映射后的灰度值),我們可以通過查表得知與輸入像素對應的輸出像素 的值。我們只需要把這種變換應用到圖像上就可以了。
img2 = cdf[img] # img為原圖像這個不知道大家理不理解,其實img的灰度值 成了cdf的索引,然后重新生成一幅新的圖像(也就是均衡化后的圖像).
最后寫一個代碼:
import cv2 import numpy as np from matplotlib import pyplot as pltimg = cv2.imread("src.jpg", 0)hist, bins = np.histogram(img.ravel(), 256, [0, 256])cdf = np.cumsum(hist)# 構建 Numpy 掩模數組,cdf 為原數組,當數組元素為 0 時,掩蓋(計算時被忽略)。 cdf_m = np.ma.masked_equal(cdf,0)# 均衡化公式 cdf_m = (cdf_m - cdf_m.min())*255/(cdf_m.max()-cdf_m.min())# 對被掩蓋的元素賦值,這里賦值為 0 cdf = np.ma.filled(cdf_m,0).astype('uint8')img2 = cdf[img]hist2, bins2 = np.histogram(img2.ravel(), 256, [0, 256])cdf2 = np.cumsum(hist2) cdf_equal_normalized = cdf2 * max(hist2) / max(cdf2)plt.hist(img2.ravel(), 256, [0, 256]) plt.plot(cdf_equal_normalized, "r")cv2.imwrite("equal.jpg", img2) plt.savefig("histogram.jpg", dpi = 300, bbox_inches = "tight")plt.show()圖12 均衡化后的圖像與對應的直方圖參考:
https://bk.tw.lvfukeji.com/wiki/%E7%9B%B4%E6%96%B9%E5%9B%BE%E5%9D%87%E8%A1%A1%E5%8C%96?bk.tw.lvfukeji.com是不是感覺上面的直方圖均衡化算法有點難,其實OpenCV提供了直方圖均衡化算法.
equ = cv2.equalizeHist(img)一個函數就搞定了,哈哈,說這么多,其實就是在說一個函數,相關的代碼如下:
import cv2 import numpy as npimg = cv2.imread("Origianl.jpg", 0)equ = cv2.equalizeHist(img) res = cv2.hstack((img, equ)) #stacking images side-by-sidecv2.imshow("demo", res)cv2.waitKey() cv2.destroyAllWindows()效果如下:
圖13 OpenCV的直方圖均衡化Q2: 大家有沒有想過,為什么變換函數是形如CDF的形式,難道是憑空想出來的 ?
A2: 顯然不是,下面我們一起來分析一下.
假設我們有一張圖像
,其直方圖分布 ,我們想利用一個函數映射: ,將圖像 變為圖像 ,即對圖像 每個像素點施加 變換,就會得到圖像 的直方圖分布為 .整個過程可按下圖13的圖示說明:圖中右下方是
圖像的灰度直方圖分布,圖中右上方是單調非線性變換函數 ,左上方通過映射得到的圖像 的直方圖分布,其中有 , .于是可以理解
的作用是將 圖像里面像素點灰度為 的全部變為 ,那么則有:上面公式可以理解為對應的區間內像素點總數不變. 為實現直方圖均衡化,特殊地有:
其實在這里,我自己有一個疑問:區間 與區間 內的像素點總數是相等的,這點確實如此,但是,這跟積分求和,好像沒有多大的關系啊,在數學中,積分就是求面積(無窮個小矩形的面積相加,這是比較好理解的),在物理中,拿速度與時間進行舉例,積分就是路程(無窮個小區間的路程相加,就是整個區間的路程),而本題中, ,這個我不清楚它代表的是什么?好像沒有實際的意義.不知道有沒有人清楚,這個該怎么解釋?
經查資料,其實這個積分可以理解為概率,我們可以把灰度與灰度出現的次數(要進行相應的歸一化處理),看成概率密度函數,某一灰度出現的次數越多,代表它的概率密度越大(注意并不是概率越大,連續型隨機變量,概率是要區間的,單說某一變量的概率并沒有意義).
因為我們的目標是直方圖均勻化,那么理想的有
,其中 :圖像的像素點個數, :灰度級數,灰度圖像的位深為 , . 那么可以得到:便可以解得:
離散形式得:
圖13 直方圖均衡化示意圖通過上面推導過程可以看出,映射函數
和CDF是密切存相關. 并且,推導過程在是連續上作的處理,而實際上圖像灰度級為256,故是一種以離散情況近似連續分布,如果灰度級別足夠高,產生的結果會是真正均勻分布的直方圖,然而實際得到的直方圖往往不是均勻分布的,而是近似均勻分布. 同時,假設A圖像的灰度直方圖變化劇烈,且某些灰度區間不存在像素點,這會造成CDF劇烈變化 ,從而導致最后產生的B圖像的直方圖也有較大的不均勻性. 但,實際運用過程中,得到的圖像能近似均勻分布已經能達到比較好的對比度增強效果.參考:
李新春:直方圖均衡化?zhuanlan.zhihu.comybai62868:說說直方圖均衡化?zhuanlan.zhihu.com總結
以上是生活随笔為你收集整理的不调用python函数实现直方图均衡化_直方图均衡化(HE)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 朝鲜潜艇首次成功试射“战略巡航导弹
- 下一篇: python实现b树_B树及2-3树的p