Histogram Equalization - 直方圖均衡化 詳細解說 # 附 Python 程式碼
[影像處理 Image Processing]
Histogram Equalization - 直方圖均衡化 詳細解說 # 附 Python 程式碼
前言介紹
Histogram Equalization 直方圖均衡化 - 是用於將一幅圖像的像素的色彩強度平均分佈,令圖像提高對比度及擁有更豐富的色彩,能使圖像不會過暗或過亮,常用於過暗或過亮的圖片美化。
觀念解說
Histogram Equalization 主要目的是將原始圖像像素的色彩強度均勻地映射到整個色彩範圍內,得到一個色彩強度分佈均勻的圖像。
以下用Lenna來作為範例。
         
       | 
    
| Histogram Equalization 直方圖均衡化 效果圖 | 
經過比較可以發現經過直方圖均衡化後,使圖像原本較亮的地方更亮,較暗的地方更暗,色彩變得比較均衡。圖像的對比度增強,使得細節更加清晰。
若是應用在較暗的圖片,就會呈現出以下這種效果。
           
         | 
      
| Histogram Equalization 直方圖均衡化- 較暗的圖片效果 | 
計算方法
假設圖像A 是一個 5 x 5 像素的圖像,其色彩範圍是 [ 0 , 7 ]。
           
         | 
      
| 圖像A | 
然後計算其統計直方圖如下表 - 表格A︰
| 像素值(級) | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 
| 像素個數 | 7 | 5 | 1 | 3 | 4 | 1 | 1 | 3 | 
得出統計直方圖後,我們需要將此統計直方圖歸一化,將數值範圍縮放到 0 至 1 之間,簡單來說其實就是將像素值的出現次數轉換成機率而己,目的是為了得出新的統計直方圖的像素值(可以理解成新的色彩值)。
出現機率 = 出現次數 / 像素總數
| 像素值(級) | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 
| 像素個數 | 7 | 5 | 1 | 3 | 4 | 1 | 1 | 3 | 
| 出現機率 | 7/25 | 5/25 | 1/25 | 3/25 | 4/25 | 1/25 | 1/25 | 3/25 | 
出現機率 = 出現次數 / 像素總數
| 像素值(級) | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 
| 像素個數 | 7 | 5 | 1 | 3 | 4 | 1 | 1 | 3 | 
| 出現機率 | 0.28 | 0.2 | 0.04 | 0.12 | 0.16 | 0.04 | 0.04 | 0.12 | 
得出歸一化統計直方圖後,接下來需要計算出累計直方圖 ( 臺灣應該是稱作累積相對次數分配折線圖 ? ) ,即計算所有像擦值的累計機率,結果如下表所示。
| 像素值(級) | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 
| 像素個數 | 7 | 5 | 1 | 3 | 4 | 1 | 1 | 3 | 
| 出現機率 | 0.28 | 0.2 | 0.04 | 0.12 | 0.16 | 0.04 | 0.04 | 0.12 | 
| 累計機率 | 0.28 | 0.48 | 0.52 | 0.64 | 0.8 | 0.84 | 0.88 | 1.00 | 
在得出累計直方圖後,就可以對原有的像素值(級)進行轉換了,使得可以在原有的像素值範圍[ 0 - 7 ]內對像素值(色彩度)實現均衡化。亦可以將統計數值映射到更大的色彩空間範圍內,對色彩度實現均衡化,下面分別會介紹這兩種形式。
兩種形式:
1. 在原有範圍內實現均衡化
2. 映射到更廣泛的色彩空間範圍內且實現均衡化
1. 在原有範圍內實現均衡化
在原有色彩範圍內實現均衡化,只需要將累計機率乘以當前的像素值的最大值7,就可以得到新的像素值(級)作為均衡化的結果。下表所示的就是計算得到的新像素值(級)
| 像素值(級) | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 
| 像素個數 | 7 | 5 | 1 | 3 | 4 | 1 | 1 | 3 | 
| 累計機率 | 0.28 | 0.48 | 0.52 | 0.64 | 0.8 | 0.84 | 0.88 | 1.00 | 
| 均衡化值 (新的像素值)  | 
          0.28 x 7  = 1.96  | 
          0.48 x 7  = 3.36  | 
          0.52 x 7 = 3.64  | 
          4.48 | 5.6 | 5.88 | 6.16 | 7.00 | 
我們可以看到上圖計算結果都是具有小數點的,所以需要四捨五入取得整數,才能成為均衡化值。
| 像素值 (級) | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 
| 像素個數 | 7 | 5 | 1 | 3 | 4 | 1 | 1 | 3 | 
| 出現機率 | 0.28 | 0.2 | 0.04 | 0.12 | 0.16 | 0.04 | 0.04 | 0.12 | 
| 均衡化值  (新的像素值)  | 
          2 | 3 | 4 | 4 | 6 | 6 | 6 | 7 | 
- 像素值 0 均衡化後,調整為像素值 2 ,總共有7個像素點。
 - 像素值 1 均衡化後,調整為像素值 3 ,總共有5個像素點。
 - 像素值 2 和 3 均衡化後,調整為像素值 4 ,總共有4個像素點。
 - 像素值 4,5 和 6 均衡化後,調整為像素值 6 ,總共有6個像素點。
 - 像素值 7 均衡化後,調整為像素值 7 , 總共有3個像素點。
 
得出下方結果︰
| 均衡化值 (新的像素值)  | 
          0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 
| 像素個數 | 0 | 0 | 7 | 5 | 4 | 0 | 6 | 3 | 
2. 映射到更廣泛的色彩空間範圍內且實現均衡化
想映射到更大的色彩空間範圍,只需要將累計機率乘以該範圍像素值的最大值就可以了。
假設要將色彩空間範圍擴展為[0, 255] 共 256 個像素級度,就必須將原累計機率乘以255,得到均衡化值。
| 像素值(級) | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 
| 像素個數 | 7 | 5 | 1 | 3 | 4 | 1 | 1 | 3 | 
| 累計機率 | 0.28 | 0.48 | 0.52 | 0.64 | 0.8 | 0.84 | 0.88 | 1.00 | 
| 
            均衡化值 (新的像素值) (尚未四捨五入)  | 
          
            0.28 x
            255 = 71.4  | 
          
            0.48 x
            255 = 122.4  | 
          132.6 | 163.2 | 204 | 214.2 | 224.4 | 255 | 
| 像素值(級) | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 
| 像素個數 | 7 | 5 | 1 | 3 | 4 | 1 | 1 | 3 | 
| 累計機率 | 0.28 | 0.48 | 0.52 | 0.64 | 0.8 | 0.84 | 0.88 | 1.00 | 
| 
            均衡化值 (新的像素值) (四捨五入)  | 
          71 | 122 | 133 | 163 | 204 | 214 | 224 | 255 | 
Python 程式碼實作
程式是讀取這張圖片進行實作,可以下載去試試運行。
  
def myHistEq(img):
    hist,bins = np.histogram(img.ravel(),256,[0,255])
    #print("hist=\n",hist) //testing
    #print("bins=\n",bins) //testing
    
    pdf = hist/img.size #hist = 出現次數。出現次數/總像素點 = 概率 (pdf)
    #print("pdf=\n",pdf) //testing
    
    cdf = pdf.cumsum() # 將每一個灰度級的概率利用cumsum()累加,變成書中寫的「累積概率」(cdf)。
    #print("cdf=\n",cdf) //testing 
    
    equ_value = np.around(cdf * 255).astype('uint8') #將cdf的結果,乘以255 (255 = 灰度範圍的最大值) ,再四捨五入,得出「均衡化值(新的灰度級)」。
    #print("equ_value=\n",equ_value) //testing
    result = equ_value[img]
    return result
#start of plt.figure()
plt.figure(figsize=(20,30))
#(plt_1) Original Image - RGB    (plt_2) Empty
plt.subplot(5,2,1) 
img_bgr = cv2.imread("Lenna_rgb.png",cv2.IMREAD_COLOR) #OpenCV default get the BGR Format                         
plt.title("Original Image - RGB")
plt.axis('off') 
plt.imshow(cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB))
#(plt_3) Original Image - Gray 
img_gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY)   #Convert BGR to Gray
plt.subplot(5,2,3)                                  # Set image location to show
plt.title("Original Image - Gray")
plt.axis('off')
plt.imshow(cv2.cvtColor(img_gray, cv2.COLOR_BGR2RGB))
#(plt_4) Histrogram - (Gray) Original Image
plt.subplot(5,2,4)
plt.title("Histrogram - (Gray) Original Image")
plt.hist(img_gray.ravel(),bins=256,range=(0,255))
#calculation
mydst = myHistEq(img_gray)   #這是我們自己設計的 Histogram Equalization 函數  defined on (line#1 - line#15)
cvdst = cv2.equalizeHist(img_gray) #cv2.equalizeHist()是 OpevCV 內建的 Histogram Equalization 函數
sse = np.sum((mydst-cvdst)**2)
#(plt_5) After Processing Image - Gray
plt.subplot(5,2,5)
plt.title("After Equalization Image - Gray (mydst)")
plt.axis('off')
plt.imshow(cv2.cvtColor(mydst,cv2.COLOR_BGR2RGB))
#(plt_6) After Processing Image - Gray
plt.subplot(5,2,6)
plt.title("After Equalization Histrogram - Gray (mydst)")
plt.hist(mydst.ravel(),bins=256,range=(0,255),color='b')
#(plt_7)
plt.subplot(5,2,7)
plt.title("After Equalization Image - Gray (cvdst)")
plt.axis('off')
plt.imshow(cv2.cvtColor(cvdst,cv2.COLOR_BGR2RGB))
#(plt_8)
plt.subplot(5,2,8)
plt.title("After Equalization Histrogram - Gray (cvdst)")
plt.hist(cvdst.ravel(),bins=256,range=(0,255),color='r')
#(plt_9)
plt.subplot(5,2,9)
plt.hist(mydst.ravel(),bins=256,range=(0,255),color='b')
plt.hist(cvdst.ravel(),bins=256,range=(0,255),color='g')
plt.title("Overlapping Contrast\n Sum Of Squared Error: "+ str(sse))
plt.show()
plt.close()
#end of plt.figure()
  
  
  程式執行結果如下︰(可點擊後在新分頁開啟)





留言
張貼留言