圖像直方圖與直方圖均衡化

圖像直方圖與直方圖均衡化

圖像直方圖以及灰度與彩色圖像的直方圖均衡化

圖像直方圖:

概述:

圖像的直方圖用來表徵該圖像像素值的分佈情況。用一定數目的小區間(bin)來指定表徵像素值的範圍,每個小區間會得到落入該小區間表示範圍的像素數目。

圖像直方圖圖形化顯示不同的像素值在不同的強度值上的出現頻率,對於灰度圖像來說強度範圍爲[0~255]之間,對於RGB的彩色圖像可以獨立顯示三種顏色的圖像直方圖。

同時直方圖是用來尋找灰度圖像二值化閾值常用而且是有效的手段之一,如果一幅灰度圖像的直方圖顯示爲兩個波峯,則二值化閾值應該是這兩個波峯之間的某個灰度值。

並且直方圖是調整圖像對比度的重要依據,直方圖拉伸和直方圖均衡化是兩種最常見的間接對比度增強方法。

下面我將介紹如何用python繪製圖像直方圖,以及直方圖均衡化的原理並給出灰度圖與彩色圖的直方圖均衡化。

使用python繪製圖像直方圖:

在之前寫的《python基本圖像操作》中已經提到了如何用python去繪製圖像的直方圖,python的優點就是具有衆多擴展庫和更加易用,所以可以避免陷入算法細節從更宏觀的角度實現自己的想法,所以用python繪製直方圖非常簡單:

使用Matplotlib繪製直方圖

(灰度)圖像的直方圖可以使用 hist() 函數繪製:

hist() 函數的第二個參數指定小區間的數目。需要注意的是,因爲 hist() 只接受一維數組作爲輸入,所以我們在繪製圖像直方圖之前,必須先對圖像進行壓平處理。flatten() 方法將任意數組按照行優先準則轉換成一維數組。

# -*- coding: utf-8 -*-

from PIL import Image

from pylab import *

# 讀取圖像到數組中,並灰度化
im = array(Image.open('./source/test.jpg').convert('L'))

# 直方圖圖像
hist(im.flatten(),128)

# 顯示
show()

測試圖片

Test.jpg

這裏寫圖片描述

運行結果

這裏寫圖片描述

直方圖的原理也很簡單了,自己實現也非常簡單,所以也沒有必要再過多敘述,具體細節可以去看一下C++的實現。

直方圖均衡化(Histogram Equalization):

概述:

直方圖均衡化是非常有用的一種變換,直方圖均衡化是指將一幅圖像的灰度直方圖變平,使變換後的圖像中每個灰度值的分佈概率都相同。在對圖
像做進一步處理之前,直方圖均衡化通常是對圖像灰度值進行歸一化的一個非常好的方法,並且可以增強圖像的對比度。

如果一副圖像的像素佔有很多的灰度級而且分佈均勻,那麼這樣的圖像往往有高對比度和多變的灰度色調。直方圖均衡化就是一種能僅靠輸入圖像直方圖信息自動達到這種效果的變換函數。它的基本思想是對圖像中像素個數多的灰度級進行展寬,而對圖像中像素個數少的灰度進行壓縮,從而擴展像原取值的動態範圍,提高了對比度和灰度色調的變化,使圖像更加清晰。

圖像對比度增強的方法可以分成兩類:一類是直接對比度增強方法;另一類是間接對比度增強方法。直方圖拉伸和直方圖均衡化是兩種最常見的間接對比度增強方法。直方圖拉伸是通過對比度拉伸對直方圖進行調整,從而“擴大”前景和背景灰度的差別,以達到增強對比度的目的,這種方法可以利用線性或非線性的方法來實現;直方圖均衡化則通過使用累積函數對灰度值進行“調整”以實現對比度的增強。

直方圖均衡化是圖像處理領域中利用圖像直方圖對對比度進行調整的方法。這種方法通常用來增加許多圖像的局部對比度,尤其是當圖像的有用數據的對比度相當接近的時候。通過這種方法,亮度可以更好地在直方圖上分佈。這樣就可以用於增強局部的對比度而不影響整體的對比度,直方圖均衡化通過有效地擴展常用的亮度來實現這種功能。

這種方法對於背景和前景都太亮或者太暗的圖像非常有用,這種方法尤其是可以帶來X光圖像中更好的骨骼結構顯示以及曝光過度或者曝光不足照片中更好的細節。這種方法的一個主要優勢是它是一個相當直觀的技術並且是可逆操作,如果已知均衡化函數,那麼就可以恢復原始的直方圖,並且計算量也不大。
這種方法的一個缺點是它對處理的數據不加選擇,它可能會增加背景雜訊的對比度並且降低有用信號的對比度;變換後圖像的灰度級減少,某些細節消失;某些圖像,如直方圖有高峯,經處理後對比度不自然的過分增強。

實現:

直方圖均衡化處理的“中心思想”是把原始圖像的灰度直方圖從比較集中的某個灰度區間變成在全部灰度範圍內的均勻分佈。直方圖均衡化就是對圖像進行非線性拉伸,重新分配圖像像素值,使一定灰度範圍內的像素數量大致相同。直方圖均衡化就是把給定圖像的直方圖分佈改變成“均勻”分佈直方圖分佈。

直方圖均衡化的基本思想是把原始圖的直方圖變換爲均勻分佈的形式,這樣就增加了象素灰度值的動態範圍從而可達到增強圖像整體對比度的效果。設原始圖像在(x,y)處的灰度爲f,而改變後的圖像爲g,則對圖像增強的方法可表述爲將在(x,y)處的灰度f映射爲g。在灰度直方圖均衡化處理中對圖像的映射函數可定義爲:g = EQ (f),這個映射函數EQ(f)必須滿足兩個條件(其中L爲圖像的灰度級數):

  • (1)EQ(f)在0≤f≤L-1範圍內是一個單值單增函數。這是爲了保證增強處理沒有打亂原始圖像的灰度排列次序,原圖各灰度級在變換後仍保持從黑到白(或從白到黑)的排列。

  • (2)對於0≤f≤L-1有0≤g≤L-1,這個條件保證了變換前後灰度值動態範圍的一致性。

累積分佈函數(cumulative distribution function,CDF)即可以滿足上述兩個條件,並且通過該函數可以完成將原圖像f的分佈轉換成g的均勻分佈。此時的直方圖均衡化映射函數爲:

累計分佈函數(CDF)就是是概率密度函數(probability density function/pdf)的積分,相信學過概率論的同學對他一定不陌生:

累積分佈函數(cumulative distribution function)定義 對連續函數,所有小於等於a的值,其出現概率的和。F(a)=P(x<=a)

所以總結一下灰度圖直方圖均衡化算法的步驟就是:

  • (1) 根據輸入的灰度圖像計算其原始直方圖
  • (2) 對輸入的原始直方圖進行累加,計算其cdf
  • (3) 使用累計分佈函數的插值計算新的灰度值(這裏我使用比較常用的線性插值)

使用python及其數學庫的話求直方圖和cdf的方法已經爲我們封裝好了,所以實現起來代碼量是很少的:

# -*- coding: utf-8 -*-

from PIL import Image

from pylab import *

#讀取圖像到數組中,並灰度化
im = array(Image.open('./source/test.jpg').convert('L'))

#繪製原始直方圖
subplot(231)

hist(im.flatten(),256)

#計算圖像直方圖(每個bins數組的區間值對應一個imhist數組中的強度值)
imhist,bins = histogram(im.flatten(),256,normed=True)

#計算累積分佈函數
cdf = imhist.cumsum()

#累計函數歸一化(由0~1變換至0~255)
cdf = cdf*255/cdf[-1]

#繪製累計分佈函數
subplot(232)

plot(bins[:256],cdf)

#依次對每一個灰度圖像素值(強度值)使用cdf進行線性插值,計算其新的強度值
#interp(x,xp,yp) 輸入原函數的一系列點(xp,yp),使用線性插值方法模擬函數並計算f(x)
im2 = interp(im.flatten(),bins[:256],cdf)

#將壓平的圖像數組重新變成二維數組
im2 = im2.reshape(im.shape)

# 顯示均衡化之後的直方圖圖像
subplot(233)

hist(im2.flatten(),256)

#顯示原始圖像
gray()

subplot(234)

imshow(im)

#顯示變換後圖像
subplot(236)

imshow(im2)

show()

運行結果

這裏寫圖片描述

其中線性插值公式是最常用、最簡單的插值公式。線性插值和雙線性插值是圖形學中常用的技術,旋轉變換後圖片會有一些不連續點,就是通過雙線性插值法解決的,之後我會單獨寫一篇博客進行詳細介紹,這裏簡單介紹一下線性插值的原理:

處理分離的數據,如果想知道分離點之間的某些值,需要用到某種類型的插值。

這裏寫圖片描述

使用線性插值,通過連接兩點的線段找到X=2.7對應的Y值

0.7* (maxY-min Y)+minY=0.7*(20-10)+10=0.7*10+10=17

(14-minX)/(maxX-minX) =(14-13)/(16-13)=0.33

0.33* (maxY-minY)+minY=0.33*(46-35)+35=0.33*11+35=3.67+35=38.67

順便說一下,基本所有的線性插值返回的都是浮點數。

所以我們使用插值技術利用離散的cdf得出新的灰度強度值。

彩色圖像的直方圖均衡化(Histogram Equalization):

一幅彩色圖像由RGB三色通道構成,每個通道都是描述了該種顏色的強度(0-255)的一幅灰度圖,所以比較簡單的應用於彩色圖像的均衡化就是把每個顏色通道均衡化之後進行合成,下面是我的具體實現:

# -*- coding: utf-8 -*-

from PIL import Image

from pylab import *

import copy

# 讀取圖像到數組中
im = array(Image.open('./source/test.jpg'))

#獲取通道
r = im[:,:,0]

g = im[:,:,1]

b = im[:,:,2]

#顯示各個通道原始直方圖,均值化之後的直方圖以及累計分佈函數
figure()

#計算各通道直方圖
imhist_r,bins_r = histogram(r,256,normed=True)
imhist_g,bins_g = histogram(g,256,normed=True)
imhist_b,bins_b = histogram(b,256,normed=True)

subplot(331)

hist(r.flatten(),256)

subplot(332)

hist(g.flatten(),256)

subplot(333)

hist(b.flatten(),256)


#各通道累積分佈函數
cdf_r = imhist_r.cumsum()
cdf_g = imhist_g.cumsum()
cdf_b = imhist_b.cumsum()

#累計函數歸一化(由0~1變換至0~255)
cdf_r = cdf_r*255/cdf_r[-1]
cdf_g = cdf_g*255/cdf_g[-1]
cdf_b = cdf_b*255/cdf_b[-1]

#繪製累計分佈函數

subplot(334)

plot(bins_r[:256],cdf_r)

subplot(335)

plot(bins_g[:256],cdf_g)

subplot(336)

plot(bins_b[:256],cdf_b)

#繪製直方圖均衡化之後的直方圖

im_r = interp(r.flatten(),bins_r[:256],cdf_r)

im_g = interp(g.flatten(),bins_g[:256],cdf_g)

im_b = interp(b.flatten(),bins_b[:256],cdf_b)


# 顯示直方圖圖像
subplot(337)

hist(im_r,256)

subplot(338)

hist(im_g,256)

subplot(339)

hist(im_b,256)

#顯示原始通道圖與均衡化之後的通道圖
figure()

gray()

#原始通道圖
im_r_s = r.reshape([im.shape[0],im.shape[1]])

im_g_s = g.reshape([im.shape[0],im.shape[1]])

im_b_s = b.reshape([im.shape[0],im.shape[1]])



#均衡化之後的通道圖
im_r = im_r.reshape([im.shape[0],im.shape[1]])

im_g = im_g.reshape([im.shape[0],im.shape[1]])

im_b = im_b.reshape([im.shape[0],im.shape[1]])

subplot(231)

imshow(im_r_s)

subplot(232)

imshow(im_g_s)

subplot(233)

imshow(im_b_s)


subplot(234)

imshow(im_r)

subplot(235)

imshow(im_g)

subplot(236)

imshow(im_b)

#顯示原始圖像與均衡化之後的圖像
figure()

#均衡化之後的圖像
im_p = copy.deepcopy(im)

im_p[:,:,0] = im_r

im_p[:,:,1] = im_g

im_p[:,:,2] = im_b

subplot(121)

imshow(im)

subplot(122)

imshow(im_p)

show()

運行結果

figure1:RGB通道的原始直方圖,cdf,均衡化後的直方圖
這裏寫圖片描述

figure2:RGB通道的原始圖像,均衡化後的圖像
這裏寫圖片描述

figure3:原始圖像,均衡化後的圖像
這裏寫圖片描述

結語:

本篇博客介紹了圖像直方圖和圖像直方圖均衡化的原理和方法,希望我的博客對大家有所幫助~

發佈了87 篇原創文章 · 獲贊 204 · 訪問量 76萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章