1. 程式人生 > 實用技巧 >影象灰度變換:直方圖均衡和線性變換

影象灰度變換:直方圖均衡和線性變換

灰度變換是指根據某種目標條件,按一定變換關係逐點改變源影象中每一個畫素灰度值的方法,目的是為了改善畫質,使影象的顯示效果更加清晰。影象的灰度變換處理是影象增強處理技術中的一種非常基礎、直接的空間域影象處理方法,也是影象數字化軟體和影象顯示軟體的一個重要組成部分。

本文通過實現直方圖均衡線性變換分別對影象進行處理,研究其原理及效果。

1. 直方圖均衡

演算法流程

  1. 統計各灰度級的畫素點個數,並計算從 0 到 255 的累積值。
  2. 根據頻率的累積分佈計算得到各灰度級變換後的灰度級。
  3. 對原影象的每個畫素點進行對映變換。

主要程式碼

void equalizeHist(const Mat &srcImg, Mat &dstImg) {
    dstImg.create(srcImg.size(), srcImg.type());
    
    uchar *srcImgData = srcImg.data;
    uchar *dstImgData = dstImg.data;
    
    int *hist = new int[256];
    memset(hist, 0, 256 * sizeof(int));
    int pixNum = srcImg.cols * srcImg.rows;
    for (int i = 0; i < pixNum; i++)
        hist[srcImgData[i]]++;
    for (int i = 1; i < 256; i++)
        hist[i] += hist[i-1];
    
    uchar *map = new uchar[256];
    for (int i = 0; i < 256; i++)
        map[i] = cvRound((double)hist[i] / pixNum * 255);
    
    for (int i = 0; i < pixNum; i++)
        dstImgData[i] = map[srcImgData[i]];
}

引數說明:

  • srcImg:原影象
  • dstImg:直方圖均衡後的影象

執行結果分析

首先對 Lena 的這張經典人物肖像進行測試,可以看到直方圖均衡後的圖片有了明顯的變化,亮度分佈更加均勻,對比度得到增強。

為了更加直觀地觀察灰度的變化,把均衡前後的直方圖也生成出來,發現灰度的分佈的確變得均勻了。

原影象
直方圖均衡後的影象
原影象的直方圖
均衡後的直方圖

在這張風景圖上,直方圖均衡的效果更加明顯,原本像是蒙了一層紗的畫面變得清晰,更多區域性細節被顯現出來。

原影象
直方圖均衡後的影象
原影象的直方圖
均衡後的直方圖

2. 線性變換

演算法流程

  1. 計算各灰度級做線性變換後的灰度級。
  2. 對原影象的每個畫素點進行對映變換。

主要程式碼

void linearTransform(const Mat &srcImg, Mat &dstImg, double k, double b) {
    dstImg.create(srcImg.size(), srcImg.type());
    
    uchar *srcImgData = srcImg.data;
    uchar *dstImgData = dstImg.data;
    
    uchar *map = new uchar[256];
    for (int i = 0; i < 256; i++) {
        int temp = cvRound(k * i + b);
        if (temp > 255) temp = 255;
        else if (temp < 0) temp = 0;
        map[i] = temp;
    }
    
    int pixNum = srcImg.cols * srcImg.rows;
    for (int i = 0; i < pixNum; i++)
        dstImgData[i] = map[srcImgData[i]];
}

引數說明:

  • srcImg:原影象
  • dstImg:直方圖均衡後的影象
  • k:斜率
  • b:截距

執行結果分析

觀察了一些圖片後不難注意到,那些視覺效果不太好的影象往往都是灰濛濛的,在直方圖上表現出灰度大量集中在中間區域,而兩邊最黑和最白的等級卻幾乎沒有。由此很自然地聯想到,霧霾天拍攝的影象也是這樣的質感,是否可以通過線性變換的方法拉伸灰度分佈的範圍,從而儘可能地還原影象的資訊呢?

這裡用兩張霧霾天的影象進行說明。

影象一:k = 3.4 b = -280.0

原影象
線性變換後的影象
原影象的直方圖
線性變換後的直方圖

影象二:k = 3.2 b = -350.0

原影象
線性變換後的影象
原影象的直方圖
線性變換後的直方圖

可以看到線性變換的效果還是非常明顯的,原本被霧霾隱去的資訊一定程度上顯現了出來。

關於引數

以上兩張圖線性變換後的影象都是經過多次試驗後得到的,引數 kb 的選擇是變換後圖像效果好壞的關鍵。在進行了更多影象的試驗後,總結出以下幾點:

  • k > 1 ,拉伸灰度分佈,影象對比度增加;
  • 0 < k < 1 ,壓縮灰度分佈,影象對比度減小;
  • b > 0 ,灰度值向上平移,影象整體變亮;
  • b < 0 ,灰度值向下平移,影象整體變暗。

對於上述兩張影象,我們需要增強其對比度,因此 k > 1 ,至於具體取多少,要根據影象原本的灰度分佈。比如第一張影象灰度分佈很集中,因此 k 要大一些;而第二張影象灰度分佈得更寬一些,因此 k 應該適當取小一點。引數 b 的作用是使灰度整體平移,調節由於對比度增強導致的灰度偏移,使得灰度大致分佈在中央的位置。

事實上第二張影象為了儘可能地增大對比度,引數 k 已經取得相對過大了。由於程式碼中對小於 0 和大於 255 的灰度值進行了截斷,因此直方圖中顯示出大量的畫素點集中在灰度為 0 和 255 的兩側,而中間的灰度值卻很少。在影象中表現為原本不同等級的較黑的區域全部變成最黑,同時不同等級的較白的區域全部變成最白,從而可能導致某些資訊的丟失,這就是 k 取值過大的弊端。

總的來說,首先要根據灰度原本的分佈取一個儘可能大而又不至於導致灰度兩側堆積的 k ,然後通過調節 b 使得灰度分佈在中央。如果線性變換後灰度分佈均衡地佔滿了所有的灰度等級,那麼影象的視覺效果應當是比較好的。

3. 總結

直方圖均衡和線性變換都可以使影象的對比度得到增強,從而顯示出更多的細節資訊,一定程度上起到改善影象視覺效果的作用。而後者由於引數可調,因而對影象的處理更加自由,例如分段線性變換還可以根據感興趣的灰度區間,對不同的灰度範圍進行不同的對映處理。

完整原始碼請見 GitHub 倉庫