OpenCV成長之路:圖像直方圖

一、圖像直方圖的概念

圖像直方圖是反映一個圖像像素分佈的統計表,其實橫座標代表了圖像像素的種類,可以是灰度的,也可以是彩色的。縱座標代表了每一種顏色值在圖像中的像素總數或者佔所有像素個數的百分比。

圖像是由像素構成,因爲反映像素分佈的直方圖往往可以作爲圖像一個很重要的特徵。在實際工程中,圖像直方圖在特徵提取、圖像匹配等方面都有很好的應用。

二、利用OpenCV計算圖像的直方圖

OpenCV中計算圖像直方圖像函數是calcHist,它的參數比較多,下面分析一下它的接口和用法。

void calcHist(const Mat* images, int nimages, const int* channels, InputArray mask, OutputArray hist, int dims, const int*histSize, const float** ranges, bool uniform=true, bool accumulate=false );

const Mat* images:爲輸入圖像的指針。

int nimages:要計算直方圖的圖像的個數。此函數可以爲多圖像求直方圖,我們通常情況下都只作用於單一圖像,所以通常nimages=1。

const int* channels:圖像的通道,它是一個數組,如果是灰度圖像則channels[1]={0};如果是彩色圖像則channels[3]={0,1,2};如果是隻是求彩色圖像第2個通道的直方圖,則channels[1]={1};

IuputArray mask:是一個遮罩圖像用於確定哪些點參與計算,實際應用中是個很好的參數,默認情況我們都設置爲一個空圖像,即:Mat()。

OutArray hist:計算得到的直方圖

int dims:得到的直方圖的維數,灰度圖像爲1維,彩色圖像爲3維。

const int* histSize:直方圖橫座標的區間數。如果是10,則它會橫座標分爲10份,然後統計每個區間的像素點總和。

const float** ranges:這是一個二維數組,用來指出每個區間的範圍。

後面兩個參數都有默認值,uniform參數表明直方圖是否等距,最後一個參數與多圖像下直方圖的顯示與存儲有關。

下面我們來計算一幅圖像的灰度直方圖,彩色直方圖以及自定義的灰度分佈圖。

灰度直方圖:

int main()
{
    Mat Image=imread("../cat.png");
    cvtColor(Image,Image,CV_BGR2GRAY);
    const int channels[1]={0};
    const int histSize[1]={256};
    float hranges[2]={0,255};
    const float* ranges[1]={hranges};
    MatND hist;
    calcHist(&Image,1,channels,Mat(),hist,1,histSize,ranges);
    return 0;
}

彩色直方圖:

int main()
{
    Mat Image=imread("../cat.png");
    const int channels[3]={0,1,2};
    const int histSize[3]={256,256,256};
    float hranges[2]={0,255};
    const float* ranges[3]={hranges,hranges,hranges};
    MatND hist;
    calcHist(&Image,1,channels,Mat(),hist,3,histSize,ranges);
    return 0;
}

不均勻直方圖,我們分別統計0-50,50-80,80-150,150-230,230-255區間的灰度分佈:

int main()
{
    Mat Image=imread("../cat.png");
    cvtColor(Image,Image,CV_BGR2GRAY);
    const int channels[1]={0};
    int histSize[1]={5};
    float hranges[6]={0,50,80,150,230,255};
    const float* ranges[1]={hranges};
    MatND hist;
    calcHist(&Image,1,channels,Mat(),hist,1,histSize,ranges,false);
    return 0;
}

三、直方圖的顯示

從上面的例子中我們可以看出,直方圖計算得到的實際上是一個多維數組,這並不夠直觀,我們希望能夠像在Excel中把相關數據通過表的形式表示出來。

下面通過劃線函數來把一個灰度直方圖顯示出來:

Mat getHistImg(const MatND& hist)
{
    double maxVal=0;
    double minVal=0;
    //找到直方圖中的最大值和最小值
    minMaxLoc(hist,&minVal,&maxVal,0,0);
    int histSize=hist.rows;
    Mat histImg(histSize,histSize,CV_8U,Scalar(255));
    // 設置最大峯值爲圖像高度的90%
    int hpt=static_cast<int>(0.9*histSize);
    for(int h=0;h<histSize;h++)
    {
        float binVal=hist.at<float>(h);
        int intensity=static_cast<int>(binVal*hpt/maxVal);
        line(histImg,Point(h,histSize),Point(h,histSize-intensity),Scalar::all(0));
    }
    return histImg;
}

imageimage

四、直方圖變換

直方圖變換是圖像處理中一個很重要的概念,圖像直方圖可以反映出圖像對比度,明暗程度等特徵,所以我們可以利用直方圖的變換進行圖像畫面的調節。

直方圖變換在實際工程中的應用很廣,一些美化照片的軟件很多工具都是在圖像的直方圖上作文章,如果有讀者對這這方面感興趣的推薦:http://www.cnblogs.com/Imageshop/

下面介紹兩個簡單的直方圖變換函數:直方圖拉伸與直方圖均衡化。

如果圖像的灰度在直方圖上顯示集中在某一個區間,則說明圖像色彩單一,我們可以將其擴展到更寬的灰度範圍內讓圖像更有層次感。

變換函數:將圖像的一種灰度值經過變換得到另一個灰度。

直方圖變換的核心就是變換函數,s=T(r),r是變換前的灰度值,s是變換後的灰度值,如要我們想將[a,b]區間的灰度變換到[0,255]範圍內,則變換函數是:T(r)=255*(r-a)/(b-a)。

我們在OpenCV中創建這樣一個變換函數:

// 創建一個1*256的矢量
Mat lut(1,256,CV_8U);
for(int i=0;i<256;i++)
{
    if(lut.at<uchar>(i)<imin)
        lut.at<uchar>(i)=0;
    else if(lut.at<uchar>(i)>imax)
        lut.at<uchar>(i)=255;
    else
        lut.at<uchar>(i)=static_cast<uchar>(
        255.0*(i-imin)/(imax-imin)+0.5);
}

其中imax,imin是圖像中的最小灰度與最大灰度。我們可以從直方圖中求出:

int imax,imin;
for(imin=0;imin<256;imin++)
{
    if(hist.at<uchar>(imin)>minValue)
        break;
}
for(imax=255;imax>-1;imax--)
{
    if(hist.at<uchar>(imax)>minValue)
        break;
}

最後我們應用OpenCV中的LUT函數,把變換應用在直方圖上即可。

LUT(image,lut,result);

第二個參數就像一個查找表一樣,將原圖像中的灰度按表查找,然後把灰度值替換爲表中對應的值。

有了上面灰度拉伸的例子就不難理解圖像的直方圖均衡了,直方圖均衡化可以讓圖像灰度分佈更加均勻,讓圖像的對比度增強。

在OpenCV中直方圖均衡不用像灰度拉伸那樣先構造一個變換函數,它有直接對應的函數,當然你如果有興趣也可以去嘗試寫一下變換函數均衡化的變換原理會稍複雜一些,在OpenCV這個系列裏面,不會太多的介紹數字圖像中的算法,以後有機會再專門來討論。

int main()
{
    Mat Image=imread("../cat.png");
    cvtColor(Image,Image,CV_BGR2GRAY);
         
    Mat result;
    equalizeHist(Image,result);
    return 0;
}

image


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章