天天看點

圖像增強 | CLAHE 限制對比度自适應直方圖均衡化

1 基本概述

CLAHE是一個比較有意思的圖像增強的方法,主要用在醫學圖像上面。之前的比賽中,用到了這個,但是對其算法原理不甚了解。在這裡做一個複盤。

CLAHE起到的作用簡單來說就是增強圖像的對比度的同時可以抑制噪聲

CLAHE的英文是Contrast Limited Adaptive Histogram Equalization 限制對比度的自适應直方圖均衡。在學習這個之前,我們要先學習一下下面的前置算法:

  1. 【Contrast Stretching】:對比度拉伸;
  2. 【HE】:直方圖均衡;
  3. 【CLHE】:對比度限制的HE
  4. 【AHE】:自适應直方圖均衡化

2 競賽中的CLAHE實作

在比賽中,我們往往使用albumentations庫函數進行圖像的預處理,因為這個預處理庫的運作速度非常的快,而且封裝了大量的圖像增強的方法。圖像任務的話這個庫函數非常滴奈斯。

本文中會介紹一下albumentations庫函數實作CLAHE的代碼,然後再用openCV實作。

import albumentations
RESIZE_SIZE = 1024 # or 768
train_transform = albumentations.Compose([
        albumentations.Resize(RESIZE_SIZE, RESIZE_SIZE),
        albumentations.OneOf([
            albumentations.RandomGamma(gamma_limit=(60, 120), p=0.9),
            albumentations.RandomBrightnessContrast(brightness_limit=0.2, contrast_limit=0.2, p=0.9),
            albumentations.CLAHE(clip_limit=4.0, tile_grid_size=(4, 4), p=0.9),
        ]),
        albumentations.HorizontalFlip(p=0.5),
        albumentations.ShiftScaleRotate(shift_limit=0.2, scale_limit=0.2, rotate_limit=20,
                                        interpolation=cv2.INTER_LINEAR, border_mode=cv2.BORDER_CONSTANT, p=1),
        albumentations.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225), max_pixel_value=255.0, p=1.0)
    ])
           

這是一個圖像增強的pipline,其中的流程是:

  • Resize就是拉伸圖檔修改尺寸
  • RandomGamma就是使用gamma變換
  • RandomBrightnessContrast就是随機選擇圖檔的對比度和亮度
  • CLAHE是一種對比度受限情況下的自适應直方圖均衡化算法
  • HorizontalFlip水準翻轉
  • ShiftScaleRotate這個就是平移縮放旋轉三合一,給力!
  • Normalize這個就是圖像歸一化了。

本文主要講解的就是CLAHE這個直方圖均衡化的算法。

3 openCV繪制直方圖

使用openCV的代碼來擷取一個圖檔的灰階直方圖:

import cv2
import numpy as np
import matplotlib.pyplot as plt

def plot(grayHist):
    plt.plot(range(256), grayHist, 'r', linewidth=1.5, c='red')
    y_maxValue = np.max(grayHist)
    plt.axis([0, 255, 0, y_maxValue]) # x和y的範圍
    plt.xlabel("gray Level")
    plt.ylabel("Number Of Pixels")
    plt.show()

if __name__ == "__main__":
    # 讀取圖像并轉換為灰階圖
    img = cv2.imread(r'E:\dog.jpg', 0)
    # 圖像的灰階級範圍是0~255
    grayHist = cv2.calcHist([img], [0], None, [256], [0, 256])

    # 繪制直方圖
    plot(grayHist)
           

狗子的圖檔就是左邊的這個,發現灰階值在100左右的像素個數最多:

4 對比度Contrast

在生活中,我們在PS圖檔的時候,往往會用到圖檔對比度,那麼這個究竟是什麼東西呢?

圖檔對比度指的是一幅圖檔中最亮的白和最暗的黑之間的反差大小。常用的定量度量方法是Michelson對比度:

\(C = \frac{I_{max}-I_{min}}{I_{max}+I_{min}}\)

  • 當一幅圖像最白和最黑像素灰階都是128時,圖像對比度最低,C=0;
  • 當一幅圖像最白像素灰階=255,最黑像素灰階=0時,圖像對比度最高,C=1.0。

【英文中如何描述高對比度與低對比度的?】

當一幅圖像最白和最黑像素灰階都在128附近浮動時,圖像的直方圖集中在中間的幾個桶,圖像看起來灰蒙蒙的,英語中使用dull描述這種效果。相反,如果圖像中黑白像素的跨度較大,則圖像富有通透感,英語中使用clarity描述這種效果。

圖檔中左邊的圖檔就是dull,灰階直方圖也是集中在中間區域,這就是低對比度;最右邊的圖檔是clarity,直方圖感覺是被拉開了、舒展了,這就是高對比度。

5 Contrast Stretching

我們已經搞懂了圖檔不通透的原因,就是灰階直方圖不夠舒展,集中在了一個小區域,這樣我們可以通過數學的方法把低對比度的圖像提高對比度。最簡單的方法就是對比度拉伸(Contrast Stretching)。

現在有這樣的一個低對比度的圖檔,其灰階直方圖集中在中間的區域。然後我們想把這個灰階直方圖拉伸到整個0~255的區間,我們怎麼做呢?(這裡假設這個低對比度的圖檔的灰階集中在100到200之間好了)

用一個這樣的分段線性函數,來處理上面那個低對比度圖檔的時候,可以把(r2,s2)極端的設定成(100,0),把(r3,s3)設定成(200,255),這樣把原來的直方圖通過這個函數映射,其實就是把100~200範圍線性拉伸到0~255這麼大。

這種方法最簡單,簡單的說就是線性拉伸直方圖。對于某些圖檔可以起到效果:

但是對于比較複雜的圖檔,并沒有什麼效果:

6 Histogram Equalization

對比度解決不了的問題,我們來用HE試試。Histogram Equalization的思想就是用數學方法重新調整像素的亮度分布,來保證直方圖具有最大的動态範圍,也就是盡可能地讓灰階直方圖是一個矩形!

其實Contrast Stretching也是做的一樣的事情,隻是它用的簡單的分段線性函數來重新映射灰階,現在用更巧妙地方法。

【定義一些數學符号】

  • \(p(x)\):調整之前的直方圖的機率密度函數
  • \(q(y)\):調整之後的直方圖的機率密度函數,可以看出來,是一個常數,是以用C來表示

因為不管怎麼轉換,機率密度函數的累積總是1,而轉換前後的取值範圍都是[0,1],是以可以得到:

\(\int_0^1{p(x)dx=\int_0^1Cdy=1}\)

(當然,這裡可以很快的算出來,C=1)

我們希望找到,一個x和y的映射關系,也就是\(y=f(x)\),不難想到,這個\(f(x)\)就應該是\(p(x)\)的累積分布函數,也就是:

\(f(x)=\int_0^xp(u)du\)

這個圖中,直覺的展示了,任何一個直方圖,隻要按照該直方圖的累積分布函數進行拉伸,就可以得到一個矩形的直方圖。

下面是一個利用這樣的方法增強對比度的例子:

可以發現,在直方圖密集的地方,就會被拉的松散

再看另外一個例子:

可以發現,使用HE之後的直方圖的累積分布函數,是一個直線

7 CLAHE

HE算法在一種情況下,效果不好,如果一個圖檔中有大塊的暗區或者亮區的話,效果非常不好。這個的原因,也非常好了解,因為HE其實要求一個圖檔中必須有10%的最亮的像素點,必須有10%第二亮的像素點,必須有10%第三亮的像素點……假設有一張純黑的圖檔,你想想經過HE處理之後,會出現什麼情況?答案就是一部分黑的像素也會被強行搞成白的

下面是一個例子,發現經過HE之後的圖檔出現了大量噪點:

【Histogram Equalization的缺點】

  1. 對于灰階非常集中的區域,直方圖會被拉的非常稀疏,進而導緻對比度增強過大,成為噪音;
  2. 一些區域調整後丢失細節

7.1 Contrast Limited HE

針對第一個問題,提出了CLHE,加入對比度限制,其實原理很簡單置直方圖分布的門檻值,将超過該門檻值的分布“均勻”分散至機率密度分布上,由此來限制轉換函數(累計直方圖)的增幅。

這樣的話,直方圖就不會出現機率密度函數過大的區域,進而避免了某些集中區域被拉得過于系數。

7.2 Adaptive HE

Adaptive HE的基本思想是将原始圖檔劃分成子區域,然後對每個子區域進行HE變換。當然,這樣做的問題應該是顯而易見的:

每一塊區域之間都會有非常大的不連續。是以為了解決這個問題,提出了優化方案雙線性插值的AHE,然後這個基礎上再使用CLHE的截斷對比度的思想,就變成了我們現在的CLAHE算法。

【使用雙線性插值的方案】

  1. 将圖像分為多個矩形塊大小,對于每個矩形塊子圖,分别計算其灰階直方圖和對應的變換函數(累積直方圖)
  2. 将原始圖像中的像素按照分布分為三種情況處理:
    • 紅色區域中的像素按照其所在子圖的變換函數進行灰階映射
    • 綠色區域中的像素按照所在的兩個相鄰子圖變換函數變換後進行線性插值得到
    • 紫色區域中的像素按照其所在的四個相鄰子圖變換函數變換後雙線性插值得到

8 結果對比與openCV實作

【這裡是openCV實作HE的方法】

img = cv.imread(r'E:\dog.jpg', 0)
equ = cv.equalizeHist(img)  # 輸入為灰階圖
res = np.hstack((img, equ))  # stacking images side-by-side
cv.imwrite('res.png',res)
           

運作結果:

【openCV實作CLAHE】

img = cv2.imread(r'E:\dog.jpg', 0)

# create a CLAHE object
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
cl1 = clahe.apply(img)
res = np.hstack((img, cl1))
cv2.imwrite('res.jpg', res)
           

結果是:

【更多對比的例子】

  • 左邊是原圖
  • 中間是HE,有過亮過暗的區域;
  • 右邊是CLAHE,沒有過亮過暗的區域。

然後我在另外一個博文,找到了上面那個例子的彩色版本哈哈:

參考文章:

  1. https://zhuanlan.zhihu.com/p/98541241
  2. https://blog.csdn.net/lwx309025167/article/details/103770834
  3. https://blog.csdn.net/u013066730/article/details/82970380
  4. https://www.cnblogs.com/imageshop/archive/2013/04/07/
  5. http://helloworld2020.net/393/