天天看點

opencv 直方圖的學習記錄

    一個圖像是由不同顔色的像素組成。像素值在圖像中的分布情況是這幅圖像的一個重要特征。我們将學習如何計算并使用直方圖來修改圖像的外觀。直方圖也可用于描述圖像的内容,并且檢測圖像中特定的對象或紋理。

    1【計算圖像的直方圖】

    圖像是由像素組成的,在一個單通道的灰階圖像中,每個像素的值介于0(黑色)~255(白色)之間。根據圖像的内容,我們會發現每個灰階值的像素數目是不同的。直方圖是一個簡單的表,它給出了一幅圖像或一組圖像中擁有給定數值的像素數量。是以,灰階圖像的直方圖有256個條目(容器)。0号容器給出值為0的像素個數,1号容器給出值為1的像素個數,以此類推。顯然,如果我們對直方圖的所有項求和,會得到像素的總數。直方圖也可以被歸一化,歸一化後的所有項之和等于1。在該情況下,每一項給出的都是擁有特定數值的像素在圖像中占的比例。

    在opencv中計算直方圖,通過cv::calcHist函數。這是一個通用函數,可以計算任意像素類型的多通道圖像。下面代碼是實作一個單通道直方圖的簡單代碼實作。

cv::MatND CowisHistogram1D::getHistogram(const cv::Mat &image)

{

    cv::MatND hist;

    cv::calcHist(&image,//輸入的圖像

 1, //計算幾張圖像

channels,//通道數量

 cv::Mat(),//掩碼

 hist, //傳回直方圖的矩陣

1, //通道數量

histSize,//項的數量

 rang);//像素值的範圍

    return hist;

}

擷取到直方圖的資料是一個矩陣,友善我們觀察,我們把資料畫出來,主要代碼如,運作的效果如下圖所示

cv::Mat CowisHistogram1D::gethistogramImage(const cv::Mat &image)

{

    cv::MatND hist = getHistogram(image);

    double maxVal  = 0;

    double minVal  = 0;

    cv::minMaxLoc(hist, &minVal, &maxVal, 0, 0);

    cv::Mat histImg(histSize[0], histSize[0], CV_8U, cv::Scalar(255));

    int hpt = static_cast<int>(0.9 * histSize[0]);

    for(int h = 0; h < histSize[0]; h++)

    {

        float binVal = hist.at<float>(h);

        int intensity = static_cast<int>(binVal * hpt / maxVal);

        cv::line(histImg, cv::Point(h, histSize[0]),cv::Point(h, histSize[0] - intensity), cv::Scalar::all(0));

    }

    return histImg;

}

opencv 直方圖的學習記錄

    從直方圖可以直覺的看到,灰階的中間位置存在一個大的峰值,同時有大量深色的像素。這兩組像素基本對應的是圖像的背景和前景。通過這兩組像素之間的過渡處進行門檻值可以證明這一點。有一個友善的函數cv::threshold函數。當需要使用一個門檻值來建立二值圖像時可以使用該函數。生成的圖像清晰的顯示了分段的背景與前景。

opencv 直方圖的學習記錄

    cv::calcHist有許多參數,大多數時候,我們的直方圖将是一個單通道或者三通道圖像。然而,該函數允許我們指定一個分布在幾個圖像中的多通道圖像。這就是為何一組圖像被作為該函數的輸入。第6個參數指定直方圖的次元,例如1指的是一維直方圖。直方圖計算中要考慮的通道列在一個數組中,它有指定的次元。在實作單通道時,預設使用的是通道0(第3個參數)。直方圖本身是通過每個次元的條目數量,每個次元的最小及最大值進行描述的,前者位于第7個參數,是一組整數,後者位于第8個參數,是一組每項包含兩個元素的數組。可以指定一個掩碼,指明哪些像素需要進行統計。還可以指定兩個額外的可選參數,都是布爾值。第一個表面直方圖是否歸一化的(預設是true),第2個值允許積累多個直方圖計算結果。如果這個參數為true,那麼圖像的像素統計值會加到輸入直方圖中目前的值上。需要主要的是,直方圖中的值是浮點數。

    下面是計算三通道直方圖的代碼。

    std::vector<cv::Mat> bgr_planes;

    cv::split(image, bgr_planes);

    cv::Mat hist1;

    cv::calcHist(&bgr_planes[0], 1, channels, cv::Mat(), hist1, 1, histSize, ranges);

    cv::Mat hist2;

    cv::calcHist(&bgr_planes[1], 1, channels, cv::Mat(), hist2, 1, histSize, ranges);

    cv::Mat hist3;

    cv::calcHist(&bgr_planes[2], 1, channels, cv::Mat(), hist3, 1, histSize, ranges);

    cv::Mat imageChann1(histSize[0], histSize[0], CV_8U, cv::Scalar(255));

    cv::Mat imageChann2(histSize[1], histSize[1], CV_8U, cv::Scalar(255));

    cv::Mat imageChann3(histSize[2], histSize[2], CV_8U, cv::Scalar(255));

    double minVal = 0.;

    double maxVal = 0.;

    cv::minMaxLoc(hist1, &minVal, &maxVal, 0 ,0);

    int hpt = static_cast<int>(0.9 * histSize[0]);

    for(int i = 0; i < 3; i++)

    {

        for(int h = 0; h < histSize[0]; h++)

        {

            if(i == 0)

            {

                float binVal = hist1.at<float>(h);

                int intensity = static_cast<int>(binVal * hpt / maxVal);

                cv::line(imageChann1, cv::Point(h, histSize[0]),cv::Point(h, histSize[0] - intensity), cv::Scalar::all(0));

            }

            else if(i == 1)

            {

                float binVal = hist2.at<float>(h);

                int intensity = static_cast<int>(binVal * hpt / maxVal);

                cv::line(imageChann2, cv::Point(h, histSize[0]), cv::Point(h, histSize[0] - intensity), cv::Scalar::all(0));

            }

            else {

                float binVal = hist3.at<float>(h);

                int intensity = static_cast<int>(binVal * hpt / maxVal);

               cv::line(imageChann3, cv::Point(h, histSize[0]), cv::Point(h, histSize[0] - intensity), cv::Scalar::all(0));

            }

        }

    }

    cv::namedWindow("Channl1");

    cv::namedWindow("Channl2");

    cv::namedWindow("Channl3");

    cv::imshow("Channl1", imageChann1);

    cv::imshow("Channl2", imageChann2);

    cv::imshow("Channl3", imageChann3);

運作結果如下

opencv 直方圖的學習記錄