OpenCV-曝光直方圖與均衡化

直方圖是我們在數據分析時常使用的表示方式,但在影像處理時又能擦出如何的火花呢?透過像素點的資訊統計,我們不僅能洞察影像的亮度、對比度等基本特性,更能進一步探索影像增強、色彩調整、影像分割等進階應用。就讓我們一起踏上這趟影像處理的探索之旅,揭開直方圖在影像世界中的奧秘。

素材取用

圖片:<雲端下載> | 清明 by DaylightAllure

實作過程

除了用於分析之外,我們也能夠透過修改直方圖來重新組合圖片,因此除了單純的圖表繪製外,我們也來探討均衡化所能帶來的效果吧。

圖形繪製

在實作過程中發現到最為困難的便是,這類的方法主要都是接收二維陣列,我們必須先把圖層分出,分開處理再合併。畢竟我們在觀察上勢必要用上色彩,因此我設計出一套複雜的方式,用來應對灰階與 RGB 圖片。

def getHistogram(img, srcTitle='原始圖像'):

    dim = len(img.shape)

    # 灰階
    if dim == 2:  
      histSize = 256
      histRange = (0, 256)
      hist = cv2.calcHist([img], [0], None, [histSize], histRange)
      hist_norm = cv2.normalize(hist, None, alpha=0, beta=1, norm_type=cv2.NORM_MINMAX)
      
      # 繪圖
      plt.figure(figsize=(10, 5))

      plt.subplot(1, 2, 1)
      plt.imshow(img, cmap="gray")
      plt.title(srcTitle)
      plt.axis('off')

      plt.subplot(1, 2, 2)
      plt.plot(hist_norm, color='gray', label='Grayscale')
    # 其他
    elif dim == 3:
      # 頻道切分
      bgr_planes = cv2.split(img.copy())

      # 數值篩選條件
      histSize = 256
      histRange = (0, 256)  # the upper boundary is exclusive
      accumulate = False

      # 計算各頻道曝光直方圖
      b_hist = cv2.calcHist(bgr_planes, [0], None, [histSize], histRange, accumulate=accumulate)
      g_hist = cv2.calcHist(bgr_planes, [1], None, [histSize], histRange, accumulate=accumulate)
      r_hist = cv2.calcHist(bgr_planes, [2], None, [histSize], histRange, accumulate=accumulate)

      # 標準化來方便檢視
      b_hist_norm = cv2.normalize(b_hist, None, alpha=0, beta=1, norm_type=cv2.NORM_MINMAX)
      g_hist_norm = cv2.normalize(g_hist, None, alpha=0, beta=1, norm_type=cv2.NORM_MINMAX)
      r_hist_norm = cv2.normalize(r_hist, None, alpha=0, beta=1, norm_type=cv2.NORM_MINMAX)

      # 繪圖
      plt.figure(figsize=(10, 5))

      plt.subplot(1, 2, 1)
      plt.imshow(img)
      plt.title(srcTitle)
      plt.axis('off')

      plt.subplot(1, 2, 2)
      plt.plot(b_hist_norm, color='blue', label='Blue Channel')
      plt.plot(g_hist_norm, color='green', label='Green Channel')
      plt.plot(r_hist_norm, color='red', label='Red Channel')
      plt.title('色彩直方')
      plt.xlabel('Pixel Value')
      plt.ylabel('Normalized Frequency')
      plt.legend()
      plt.grid(True)

    plt.tight_layout()
    plt.show()
opencv-直方圖-basic-0

我們知道 255 則代表最亮的情況,在範例中可以看到三個頻道事實上用量都非常多,由此可以率先看出色彩的鮮豔度與亮度,看出圖片是否過度或切缺曝光。而區間之間的差異則代表著對比度,最後還能從三種顏色的比例看出色調與色溫。只能說曝光直方圖是個超級實用的特徵判斷工具。

至於為何過程中我們特定標準化呢?因為比起像素點的統計總數,標準化後我們可以藉由 0~1 的分布快速看出比例,而不用額外去做換算的工作。

均衡化

色彩均衡化的概念,是透過拉伸與重新分配顏色的分布,以改善曝光不足或背光等問題。那我們的工具包中則存在著兩種實用的工具,同樣各有其優缺點,如何選擇,取決於您對影像處理的需求和偏好。

全局直方圖均衡化

第一想法肯定是直接對整張圖片進行全局性的色彩均衡化,卻可能會產生意想不到的結果。當我們調整影像的亮度分布時,最高亮度值和最低亮度值會被重新映射。原本過暗的區域變亮這很正常,但原本明亮的區域卻容易被視作過曝,但這種方式可以確保顏色的使用量較為平均。

opencv-直方圖-eq-stretch
限制對比度自適應直方圖均衡化 CLAHE

所以後來我們推出自適應直方圖均衡化 (Adaptive Histogram Equalization) 的概念,簡稱就是 AHE,透過計算圖像每個顯著區域,也就是把整個圖像切分許多小塊,局部化地重新分佈亮度值。但是這種技術有個明顯的問題,就是會放大區塊內的噪音。

於是乎我們追加對比度的限制,形成 Contrast Limited 的 AHE,也就是 CLAHE 這個更為常使用的技術。使用過程間除了設定區域大小外,還會再設定對比度的閥值。而完成處理後,觀察圖形能發現大體走勢變得相對順暢,在轉捩點變成平滑的走勢,但在每個小區則通常會有一個醒目的尖峰。

opencv-直方圖-eq-clahe

後話

我們深入了解了直方圖在影像處理中的強大功能。從基本的亮度、對比度分析,到進階的色彩均衡化和影像增強,直方圖都扮演著不可或缺的角色。它不僅是一個視覺化的工具,更是一個強大的分析利器。

透過深入理解直方圖的原理和應用,我們可以更好地處理影像,提升影像品質,並在影像分析、識別等領域取得更好的成果,熟悉這項技術也將大幅幫助特徵的捕捉工作。

參考

[1] Histograms in OpenCV — OpenCV
https://docs.opencv.org/4.x/de/db2/tutorial_py_table_of_contents_histograms.html

[2] 淺談直方圖均衡化 — Medium by Cindy Lin
https://medium.com/@cindylin_1410/%E6%B7%BA%E8%AB%87-opencv-%E7%9B%B4%E6%96%B9%E5%9C%96%E5%9D%87%E8%A1%A1%E5%8C%96-ahe%E5%9D%87%E8%A1%A1-clahe%E5%9D%87%E8%A1%A1-ebc9c14a8f96

[3] OpenCV – Histograms直方圖 — CH.Tseng 的 WordPress
https://chtseng.wordpress.com/2016/12/05/opencv-histograms%E7%9B%B4%E6%96%B9%E5%9C%96/

額外讀物

[4] CLAHE — OpenCV
https://docs.opencv.org/3.4/d6/db6/classcv_1_1CLAHE.html

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料