1. 程式人生 > >0014-OpenCV環境下計算並繪製灰度直方圖的原始碼!

0014-OpenCV環境下計算並繪製灰度直方圖的原始碼!

影象的直方圖是影象進行運算時的一個重要的資料特徵,許多演算法都需要用到影象的直方圖資料,OpenCV提供了函式calcHist用來計算影象的直方圖資料。這個函式的引數較多,下面介紹這個函式。
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:要計算影象的個數。
channel:計算哪些通道的直方圖。通道編號方法:第一張圖的通道編號為0至images[0].channels()-1,第二張圖的通道編號為images[0].channels()至images[0].channels() + images[1].channels()-1,以此類推,注意這個引數是指標。
mask:掩碼陣列。掩碼中的非0元素對應的影象元素將會被計算,0元素則被遮蔽不參與計算。掩碼陣列可以為空。
hist:直方圖計算結果儲存陣列。
dims
:直方圖的維度,比如灰度圖的直方圖為1維,H-S直方圖為2維,以此類推,最大不超過CV_MAX_DIMS,目前CV_MAX_DIMS=32。
histSize:各個維度的大小。
ranges:儲存每個維度的統計範圍。
uniform:直方圖是否均勻化的標誌,意義暫時不清楚,等以後搞清楚了再來補充說明。
accumulate:記憶標誌。表示這個函式空間被再次呼叫前是否清零,如果不清零,則可以使得使用者儲存多張圖的直方圖資料,或者對之前的計算過的直方圖進行更新。
OpenCV下計算並繪製直方圖的程式碼如下

影象處理開發資料、影象處理開發需求、影象處理接私活掙零花錢,可以搜尋公眾號"qxsf321",並關注!


原始碼中使用的影象下載連結:http://pan.baidu.com/s/1kUJMx1t 密碼:r3d3

//opencv版本:OpenCV3.0
//VS版本:VS2013
//Author:qxsf321.net

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>    
#include <opencv2/imgproc/types_c.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/highgui/highgui_c.h>

#include <iostream>

using namespace cv;
using namespace std;

int main()
{

        Mat img = imread("02.jpg");
        if (img.empty())
        {
                cout << "Error: Could not load image" << endl;
                return 0;
        }

        Mat srcImage;
        cvtColor(img, srcImage, CV_BGR2GRAY);

        imshow("【原圖的灰度圖】", srcImage);

        //為計算直方圖配置變數  
        //首先是需要計算的通道編號,就是需要計算哪個通道的直方圖(BGR空間需要確定計算,計算方法見帖子中對相關引數的說明)  
        int channels = 0;
        //然後是定義直方圖計算結果的儲存空間
        Mat dstHist;
        //接下來是直方圖的每一個維度的數目(這個數目用於將每一維度的數值分組)  
        int histSize[] = { 256 }; 
        //最後是確定每個維度的取值範圍,就是每一維度的橫座標的取值範圍  
        //首先得定義一個數組用來儲存單個維度的的取值範圍  
        float midRanges[] = { 0, 256 };
        //然後把這個陣列再放到一個二維陣列中
        const float *ranges[] = { midRanges };

        //準備工作做好後,就可以呼叫calcHis函式計算直方圖資料了
        calcHist(&srcImage, 1, &channels, Mat(), dstHist, 1, histSize, ranges, true, false);

        //calcHist函式呼叫結束後,dstHist變數中將儲存直方圖資料 

        //接下來繪製直方圖
        //首先先建立一個黑底的影象,為了可以顯示彩色,所以該繪製圖像是一個8位的3通道影象  
        Mat drawImage = Mat::zeros(Size(256, 256), CV_8UC3);

        //因為影象中的某個灰度值的總個數可能會超出所定義的影象的尺寸,所以要對個數進行歸一化處理

        //先用 minMaxLoc函式來得到計算直方圖中的最大資料,這個函式的使用方法大家一看函式原型便知
        double g_dHistMaxValue;
        minMaxLoc(dstHist, 0, &g_dHistMaxValue, 0, 0);


        //遍歷直方圖資料,對資料進行歸一化和繪圖處理
        for (int i = 0; i < 256; i++)
        {
                int value = cvRound(dstHist.at<float>(i) * 256 * 0.9 / g_dHistMaxValue);

                //line(drawImage, Point(i, drawImage.rows - 1), Point(i, drawImage.rows - 1 - value), Scalar(255, 0, 0));

                line(drawImage, Point(i,0), Point(i, value), Scalar(255, 0, 0));
        }

        //對繪製出的影象作y方向上的翻轉處理,以符合我們的看圖習慣

        // 輸出矩陣定義
        Mat resultImage(drawImage.size(), drawImage.type());
        // X與Y方向矩陣
        Mat xMapImage(drawImage.size(), CV_32FC1);
        Mat yMapImage(drawImage.size(), CV_32FC1);
        int rows = drawImage.rows;
        int cols = drawImage.cols;

        for (int j = 0; j < rows; j++)
        {
                for (int i = 0; i < cols; i++)
                {
                        // y方向上翻轉
                        xMapImage.at<float>(j, i) = i;
                        yMapImage.at<float>(j, i) = rows - j;
                }
        }
        // 重對映操作
        remap(drawImage, resultImage, xMapImage, yMapImage,
                CV_INTER_LINEAR, cv::BORDER_CONSTANT,
                cv::Scalar(0, 0, 0));

        imshow("【未翻轉前的直方圖】", drawImage);
        imshow("【翻轉後的直方圖】", resultImage);

        waitKey(0);

        return 0;
}

程式碼說明
程式碼中用到了line函式來繪製直方圖,這裡給出它的原型,大家看下原型再結合原型自然就明白了怎麼用了。
void line(InputOutputArray img, Point pt1, Point pt2, const Scalar& color,int thickness = 1, int lineType = LINE_8, int shift = 0);
另外,大家要注意,在opencv中,座標原點在左上角,也就是說向下為y軸正方向,所以程式碼中為了大家的閱讀習慣還對影象進行了一次翻轉處理。翻轉部分程式碼的原理請大家參看博文https://blog.csdn.net/lehuoziyuan/article/details/84029770
程式碼執行結果截圖如下