直方圖(calcHist)

直方圖是一個影像像素的統計表,橫軸為影像中所有可能的像素值,假使為8位元圖,範圍即為0到255,縱軸為此橫軸強度的像素個數,直方圖可以被歸一化,歸一化後所有項和為一,在這種情況下,縱軸值表示此強度的像素佔影像的比例。

histogram

直方圖是影像的一個重要特性,我們可以從這看出強度分布狀況,像是否太暗或過曝,或者分布太過集中,進而評估影像的品質。在影像檢索時,直方圖可以當成是一段獨特的紋理,或者是獨特的物體,透過直方圖的比較協助我們搜尋物體。


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)

  • images:輸入圖,可以一個或多個圖,深度必須為CV_8U或CV_32F,可為任意通道數,但是每張圖的尺寸和深度必須相同。
  • nimages:有幾張輸入圖。
  • channels:直方圖通道清單。
  • mask:可有可無的遮罩。
  • hist:輸出的直方圖
  • dims:直方圖維度,必須為正數且不能超過CV_MAX_DIMS(目前為32),假設為灰階圖的直方圖,每個像素只有強度資料,此時維度為1。
  • histSize:直方圖橫軸(也稱bin)數目。
  • ranges:直方圖的強度範圍,以8位元無負號的影像,就是[0,255]。
  • uniform:各維度取值是否一致。
  • accumulate:如果設定為true的話,在呼叫calcHist()這函式的時候,hist的內容不會被清掉,方便我們做多次的直方圖計算的累加。

繪製直方圖

OpenCV本身沒有提供繪製直方圖的函式,因此我們這邊自行撰寫drawHistImg()函式,輸入從得到的直方圖,輸出直方圖視覺化影像,流程如下:

  1. 找出輸入直方圖的最大值。
  2. 將最高點設定成輸出影像高度的0.9倍(也就是256*0.9)。
  3. 算出直方圖內各個像素強度,所對應的高度。
  4. 逐行畫出各強度的高度。

以下程式碼我們讀入一張圖,用calcHist()計算出直方圖,再用drawHistImg()將直方圖畫出:

#include <cstdio>
#include <opencv2/opencv.hpp>
using namespace cv;

void drawHistImg(const Mat &src, Mat &dst);

int main(){
    Mat src = imread("lena.jpg",CV_LOAD_IMAGE_GRAYSCALE);
    int histSize = 256;
    float range[] = {0, 255} ;
    const float* histRange = {range};
    Mat histImg;
    calcHist(&src, 1, 0, Mat(), histImg, 1, &histSize, &histRange);

    Mat showHistImg(256,256,CV_8UC1,Scalar(255));  //把直方圖秀在一個256*256大的影像上
    drawHistImg(histImg, showHistImg);
    imshow("window1", src);
    imshow("window2", showHistImg);
    waitKey(0);

    return 0;
}

void drawHistImg(const Mat &src, Mat &dst){
    int histSize = 256;
    float histMaxValue = 0;
    for(int i=0; i<histSize; i++){
        float tempValue = src.at<float>(i);
        if(histMaxValue < tempValue){
            histMaxValue = tempValue;
        }
    }

    float scale = (0.9*256)/histMaxValue;
    for(int i=0; i<histSize; i++){
        int intensity = static_cast<int>(src.at<float>(i)*scale);
        line(dst,Point(i,255),Point(i,255-intensity),Scalar(0));
    }
}

hist image

hist image

回到首頁

回到OpenCV教學


參考資料:

OpenCV 教程