1. 程式人生 > >OpenCV影象增強:直方圖拉伸和直方圖均衡化

OpenCV影象增強:直方圖拉伸和直方圖均衡化

直方圖反映了影象中畫素值的分佈情況,很多時候,影象的視覺缺陷可以根據影象的直方圖來分析。比如直方圖太窄,說明影象使用的灰度值範圍太窄;比如直方圖有一個很強烈的峰值,說明影象部分灰度值的使用頻率比其他強度值要高得多。
所以,可以通過直方圖資訊來修改影象的灰度值。如果將一種灰度修改為另一種灰度,那麼這意味著這種改變不是針對某些畫素的,而是整體性的,新的顏色值只與當前畫素的顏色值相關。這種關係,通常可以用一個對映函式來表示,稱之為查詢表。
對於灰度圖,查詢表是一個一維陣列,包含256個專案。陣列下標(0-255)代表當前灰度,下標對應的值代表當前灰度修改後的值。
OpenCV裡的查詢表函式cv::LUT(),

//Performs a look-up table transform of an array
void LUT(InputArray src, InputArray lut, OutputArray dst, int interpolation=0 )

下面介紹的直方圖拉伸和直方圖均衡化操作都會用到查詢表。程式中直方圖的計算呼叫了自定義的類函式,參見OpenCV計算和顯示影象直方圖(其實將一些演算法封裝為類很簡單,方便了呼叫和擴充套件)

直方圖拉伸

//直方圖拉伸
cv::Mat stretch(const cv::Mat& image,int minvalue=0
) { Histogram1D h; cv::Mat hist = h.getHistogram(image); //找到直方圖的左邊限值 int imin = 0; for (; imin < h.getHistSize()[0];imin++) { if (hist.at<float>(imin)>minvalue) { break; } } //找到直方圖的右邊限值 int imax = h.getHistSize()[0]-1; for
(; imax >= 0; imax--) { if (hist.at<float>(imax)>minvalue) { break; } } //建立查詢表 cv::Mat lut(1, 256, CV_8U); //構建查詢表 for (int i = 0; i < 256;i++) { if (i < imin) lut.at<uchar>(i) = 0; else if (i>imax) lut.at<uchar>(i) = 255; else { lut.at<uchar>(i) = cvRound(255.0*(i - imin) / (imax - imin)); } } cv::Mat result = Histogram1D::applyLookUp(image,lut); return result; }

至於Histogram1D::applyLookUp只是cv::LUT簡單的重新封裝,具體如下

cv::Mat Histogram1D::applyLookUp(const cv::Mat& image, const cv::Mat& lookup)
{
    cv::Mat result;
    cv::LUT(image,lookup,result);
    return result;
}

執行效果:
源影象vs拉伸直方圖後的影象
這裡寫圖片描述

這裡寫圖片描述

源影象直方圖vs拉伸後的直方圖
這裡寫圖片描述

這裡寫圖片描述

直方圖均衡化

OpenCV提供了一個易用的函式,用於直方圖均衡化處理,

//Equalizes the histogram of a grayscale image.
void equalizeHist(InputArray src, OutputArray dst)

如果只是使用,而非自己弄清楚內部的實現,閱讀可以到此為止了^-^。
實現原理是:一個完全均衡的直方圖,意味著所有箱子包含的畫素數量是相同的。其中一個必要條件就是,50%畫素的強度值小於128,25%畫素的強度值小於64,依次類推。所以p%畫素的強度值必須小於或等於255*p%。這條規則也是直方圖均衡化演算法所採用的,程式語言描述如下:

lut.at<uchar>(i) = static_cast<uchar>(p[i]*255.0/image.total());

p[i]是強度值小於或等於i的畫素數量,成為累計直方圖。total返回影象畫素總數,p[i]/image.total代表某堆灰度值的百分比。
具體實現:

cv::Mat myequalizeHist(cv::Mat& image)
{
    Histogram1D h;
    cv::Mat hist = h.getHistogram(image);
    cv::Mat lut(1, 256, CV_8U);
    float cumulate = 0;//強度值小於或等於某值的畫素數量
    for (int i = 0; i < 256; i++)
    {
        cumulate += hist.at<float>(i);
        lut.at<uchar>(i) = static_cast<uchar>(cumulate*255.0/image.total());
    }
    cv::Mat result;
    cv::LUT(image, lut, result);
    return result;

}

執行效果:
源影象vs直方圖均衡化之後的影象
這裡寫圖片描述

這裡寫圖片描述

源影象直方圖 vs均衡化之後的直方圖
這裡寫圖片描述

這裡寫圖片描述