在學習OpenCV,對calcHist函數一直很是迷茫,今天在網上找的一個很讚的介紹,來自http://blog.csdn.net/ljbsdu/article/details/7412787,就特此轉來留一份。請支持原創!
這次再深入學習一下calcHist函數,即用於計算直方圖的函數,主要是分析一下該函數的衆多的參數,看看應該如何使用,先給出一段代碼,其中包括兩部分,一部分來自opencv_tutorials中的例子,一部分來自opencv2refman中,都進行了修改,opencv版本爲2.3.1。
- #include <opencv2/core/core.hpp>
- #include <opencv2/highgui/highgui.hpp>
- #include <opencv2/imgproc/imgproc.hpp>
- #include <iostream>
- #pragma comment(lib, "opencv_core231d.lib")
- #pragma comment(lib, "opencv_highgui231d.lib")
- #pragma comment(lib, "opencv_imgproc231d.lib")
- using namespace cv;
- using namespace std;
- #define HIST_DIM1
- int main( int argc, char** argv )
- {
- #ifdef HIST_DIM1
- //----------------------example 1-------------------------------//
- Mat src, dst;
- /// Load image
- src = imread("d:/picture/lena.jpg");
- if( !src.data )
- {
- cout<<"load image failed"<<endl;
- return -1;
- }
- /// Separate the image in 3 places ( R, G and B )
- vector<Mat> rgb_planes;
- #define SHOW_HSV
- #ifdef SHOW_HSV
- Mat hsv;
- cvtColor(src, hsv, COLOR_BGR2HSV);
- split(hsv, rgb_planes );
- #else
- split(src, rgb_planes );
- #endif
- /// Establish the number of bins
- int histSize = 256;
- /// Set the ranges ( for R,G,B) )
- float range[] = { 0, 255 } ;
- const float* histRange = { range };
- bool uniform = true; bool accumulate = false;
- Mat r_hist, g_hist, b_hist;
- /// Compute the histograms:
- calcHist( &rgb_planes[2], 1, 0, Mat(), r_hist, 1, &histSize, &histRange, uniform, accumulate );
- calcHist( &rgb_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRange, uniform, accumulate );
- calcHist( &rgb_planes[0], 1, 0, Mat(), b_hist, 1, &histSize, &histRange, uniform, accumulate );
- // Draw the histograms for R, G and B
- int hist_w = 600; int hist_h = 400;
- int bin_w = cvRound( (double) hist_w/histSize );
- Mat rgb_hist[3];
- for(int i=0; i<3; ++i)
- {
- rgb_hist[i] = Mat(hist_h, hist_w, CV_8UC3, Scalar::all(0));
- }
- Mat histImage(hist_h, hist_w, CV_8UC3, Scalar(0,0,0));
- /// Normalize the result to [ 0, histImage.rows-10]
- normalize(r_hist, r_hist, 0, histImage.rows-10, NORM_MINMAX);
- normalize(g_hist, g_hist, 0, histImage.rows-10, NORM_MINMAX);
- normalize(b_hist, b_hist, 0, histImage.rows-10, NORM_MINMAX);
- /// Draw for each channel
- for( int i = 1; i < histSize; i++ )
- {
- line( histImage, Point( bin_w*(i-1), hist_h-cvRound(r_hist.at<float>(i-1)) ) ,
- Point( bin_w*(i), hist_h-cvRound(r_hist.at<float>(i)) ),
- Scalar( 0, 0, 255), 1);
- line( histImage, Point( bin_w*(i-1), hist_h - cvRound(g_hist.at<float>(i-1)) ) ,
- Point( bin_w*(i), hist_h-cvRound(g_hist.at<float>(i)) ),
- Scalar( 0, 255, 0), 1);
- line( histImage, Point( bin_w*(i-1), hist_h - cvRound(b_hist.at<float>(i-1)) ) ,
- Point( bin_w*(i), hist_h-cvRound(b_hist.at<float>(i)) ),
- Scalar( 255, 0, 0), 1);
- }
- for (int j=0; j<histSize; ++j)
- {
- int val = saturate_cast<int>(r_hist.at<float>(j));
- rectangle(rgb_hist[0], Point(j*2+10, rgb_hist[0].rows), Point((j+1)*2+10, rgb_hist[0].rows-val), Scalar(0,0,255),1,8);
- val = saturate_cast<int>(g_hist.at<float>(j));
- rectangle(rgb_hist[1], Point(j*2+10, rgb_hist[1].rows), Point((j+1)*2+10, rgb_hist[1].rows-val), Scalar(0,255,0),1,8);
- val = saturate_cast<int>(b_hist.at<float>(j));
- rectangle(rgb_hist[2], Point(j*2+10, rgb_hist[2].rows), Point((j+1)*2+10, rgb_hist[2].rows-val), Scalar(255,0,0),1,8);
- }
- /// Display
- namedWindow("calcHist Demo", CV_WINDOW_AUTOSIZE );
- namedWindow("wnd");
- imshow("calcHist Demo", histImage );
- imshow("wnd", src);
- imshow("R", rgb_hist[0]);
- imshow("G", rgb_hist[1]);
- imshow("B", rgb_hist[2]);
- #else
- //----------------------example 2-------------------------------//
- Mat src, hsv;
- if(!(src=imread("d:/picture/lena.bmp")).data)
- return -1;
- cvtColor(src, hsv, CV_BGR2HSV);
- // Quantize the hue to 30 levels
- // and the saturation to 32 levels
- int hbins = 60, sbins = 64;
- int histSize[] = {hbins, sbins};
- // hue varies from 0 to 179, see cvtColor
- float hranges[] = { 0, 180 };
- // saturation varies from 0 (black-gray-white) to
- // 255 (pure spectrum color)
- float sranges[] = { 0, 256};
- const float*ranges[] = { hranges, sranges };
- MatND hist;
- // we compute the histogram from the 0-th and 1-st channels
- int channels[] = {0, 1};
- calcHist( &hsv, 1, channels, Mat(),hist, 2, histSize, ranges,true, false );
- double maxVal=0;
- minMaxLoc(hist, 0, &maxVal, 0, 0);
- int scale = 8;
- Mat histImg = Mat::zeros(sbins*scale, hbins*scale, CV_8UC3);
- for( int h = 0; h < hbins; h++ )
- {
- for( int s = 0; s < sbins; s++ )
- {
- float binVal = hist.at<float>(h, s);
- int intensity = cvRound(binVal*255/maxVal);
- rectangle( histImg, Point(h*scale, s*scale),Point((h+1)*scale-1, (s+1)*scale-1), Scalar::all(intensity), CV_FILLED);
- }
- }
- namedWindow( "Source", 1 );
- imshow( "Source", src );
- namedWindow( "H-S Histogram", 1 );
- imshow( "H-S Histogram", histImg );
- #endif
- //-------------------------------------------------------------------------//
- waitKey(0);
- destroyAllWindows();
- return 0;
- }
上面的例子是對opencv_tutorials以及手冊中的計算直方圖的程序的修改
其中修改的:
1、原先的程序中對加載的彩色rgb圖像的通道有問題(看例子給的圖應該是在linux下的,不知道是不是因爲linux和windows下加載的不同),在windows下默認加載的通道排列順序是B-G-R,
原先的程序中是按照R-G-B順序計算的直方圖所以需要變換一下順序;
2、原先程序的histImage將參數順序弄錯了,該構造函數的第一個參數是rows行數,對應圖像的高度,即hist_h,而不是hist_w,這裏同時還將大小變換了一下
看着更舒服一些;
下面是對calcHist函數的參數介紹。
calcHist--計算矩陣的直方圖函數;
--------------------------------------------------------------------------------------------------------------
- ###---given in manual---###
- void calcHist(const Mat*arrays, int narrays, const int* channels, InputArray mask, OutputArray hist, int dims,
- const int* histSize, const float** ranges, bool uniform=true, boolaccumulate=false)
- void calcHist(const Mat*arrays, int narrays, const int* channels, InputArray mask, SparseMat& hist, int dims,
- const int* histSize, const float** ranges, bool uniform=true, boolaccumulate=false)
- ###---declaration in imgproc.hpp---###
- //! computes the joint dense histogram for a set of images.
- CV_EXPORTS 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 );
- //! computes the joint sparse histogram for a set of images.
- CV_EXPORTS void calcHist( const Mat* images, int nimages, const int* channels, InputArray mask, SparseMat& hist, int dims,
- const int* histSize, const float** ranges, bool uniform=true, bool accumulate=false );
- CV_EXPORTS_W void calcHist( InputArrayOfArrays images, const vector<int>& channels, InputArray mask, OutputArray hist,
- const vector<int>& histSize, const vector<float>& ranges,bool accumulate=false );
--------------------------------------------------------------------------------------------------------------
手冊中和頭文件中的函數聲明參數稍有不同,主要是前兩個參數,手冊中是array和narrays而頭文件聲明中是images和nimages,其實是一樣,以手冊爲準:
這裏有一個對opencv_tutorials.pdf即opencv教程的一個翻譯。
http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/histograms/histogram_calculation/histogram_calculation.html
arrays – Source arrays. They all should have the same depth, CV_8U or CV_32F , and the same size. Each of them can have an arbitrary number of channels.
- 源輸入(圖像)數組,必須是相同深度的CV_8U或者CV_32F(即uchar或者float),相同大小,每一個可以是任意通道的;
[上面的例子1中每次計算一個單通道圖像,所以直接對圖像取地址賦給了該參數]
narrays – Number of source arrays.
- 源輸入數組中的元素個數;
[例子1中只計算一幅圖像的直方圖,所以這個參數都是1]
channels – List of the dims channels used to compute the histogram. The first array channels are enumerated from 0 to arrays[0].channels()-1 , the second array channels are counted from arrays[0].channels() to arrays[0].channels()
+ arrays[1].channels()-1, and so on.
- 用來計算直方圖的通道維數數組,第一個數組的通道由0到arrays[0].channels()-1列出,第二個數組的通道從arrays[0].channels()到arrays[0].channels()+arrays[1].channels()-1以此類推;
[例子1中爲0,即第0個通道??]
mask – Optional mask. If the matrix is not empty, it must be an 8-bit array of the same size as arrays[i]. The non-zero mask elements mark the array elements counted in the histogram.
-可選的掩膜,如果該矩陣不是空的,則必須是8位的並且與arrays[i]的大小相等,掩膜的非零值標記需要在直方圖中統計的數組元素;
[例子1中爲空的Mat()]
hist – Output histogram, which is a dense or sparse dims -dimensional array.
-輸出直方圖,是一個稠密或者稀疏的dims維的數組;
[例子1中爲保存直方圖的Mat]
dims – Histogram dimensionality that must be positive and not greater than CV_MAX_DIMS (equal to 32 in the current OpenCV version).
-直方圖的維數,必須爲正,並且不大於CV_MAX_DIMS(當前的OpenCV版本中爲32,即最大可以統計32維的直方圖);
[例子1中爲1,因爲統計的是每幅單通道圖像的灰度直方圖]
histSize – Array of histogram sizes in each dimension.
- 用於指出直方圖數組每一維的大小的數組,即指出每一維的bin的個數的數組;
[因爲例子1只有1維,所以例子1中直接對int取地址作爲參數,即該維的bin的個數爲256]
ranges – Array of the dims arrays of the histogram bin boundaries in each dimension. When the histogram is uniform ( uniform =true), then for each dimension i it is enough to specify the lower (inclusive) boundary of the 0-th
histogram bin and the upper(exclusive) boundary for the last histogram bin histSize[i]-1. That is, in case of a uniform histogram each of ranges[i] is an array of 2 elements. When the histogram is not uniform ( uniform=false ), then each of ranges[i] contains
histSize[i]+1 elements:. The array elements, that are not between and , are not counted in the histogram.
- 用於指出直方圖每一維的每個bin的上下界範圍數組的數組,當直方圖是均勻的(uniform =true)時,對每一維i指定直方圖的第0個bin的下界(包含即[)L0和最後一個即第histSize[i]-1個bin的上界(不包含的即))U_histSize[i]-1,也就是說對均勻直方圖來說,每一個ranges[i]都是一個兩個元素的數組【指出該維的上下界】。當直方圖不是均勻的時,每一個ranges[i]數組都包含histSize[i]+1個元素:L0,U0=L1,U1=L1,...,U_histSize[i]-2
= L_histSize[i]-1,U_histSize[i]-1.不在L0到U_histSize[i]-1之間的數組元素將不會統計進直方圖中;
[在例子1中採用的是均勻直方圖,所以範圍爲0-255]
uniform – Flag indicates that whether the histogram is uniform or not (see above).
- 直方圖是否均勻的標誌;【指定直方圖每個bin統計的是否是相同數量的灰度級】
[例子1中爲true]
accumulate – Accumulation flag. If it is set, the histogram is not cleared in the beginning when it is allocated.
This feature enables you to compute a single histogram from several sets of arrays, or to update the histogram in time.
-累加標誌;
[單幅圖像不進行累計所以例子1中爲false]
參數中最難理解的應該就是channels和ranges這兩個參數,以及histSize和ranges這兩個參數的關係,關於histSize和ranges的關係也就涉及了ranges的意義,關於它們的關係在《學習OpenCV中文版》09.10第一版的page:219-220有比較清楚的說明。
【channels參數,自己也不是很明確,等看看該函數的源碼之後再說】。
使用上面第一個例子獲得的lena的hsv直方圖如下:直接在RGB基礎上修改的,所以窗口名字對應R-V,G-S,B-H。
手冊中該函數的介紹之後有個例子,是計算圖像的2維H-S直方圖的,就是上面的例子2(稍微進行了一點修改);
這個例子中的參數分別爲:
參數1:&hsv,一幅HSV三通道的彩色圖像指針;
參數2:1,因爲參數1是一幅圖像;
參數3:channels,數組包含兩個元素:0,1;--指明要統計的是通道0和通道1的數據??--不確定是否是這樣的!
參數4:Mat(),爲空,不使用掩膜;
參數5:hist,輸出2D直方圖,MatND,也就是Mat;
參數6:2,2維直方圖;
參數7:histSize,兩個元素的數組,指明每一維的bin的個數,上面的例子2中,h的爲60,s的爲64;
參數8:ranges,指出bin的範圍的數組的數組,因爲後面的uniform標誌爲true,也就是均勻直方圖,所以每一維由一個兩個元素的數組指出上下限;
參數9:true,也就是採用均勻直方圖;
參數10:false,不使用累積;
第二個例子給出的2維直方圖中,水平的是h分量,垂直的是s分量,下面是lena的H-S直方圖圖像:
可以看出,h分量也就是H-S直方圖的垂直投影集中在0和60左右,對應到hsv空間也就是色相爲紅色部分,從例子1的h分量直方圖以及直觀的看lena原始圖像也可以看出來,
而s分量,也就是水平投影,集中在中間部分,從例子1的s分量的直方圖中也可以看出;
而且如果將這個H-S二維直方圖的每一維的bin數量設置的與上面的例1中一樣,然後在分別向垂直方向和水平方向投影,獲得的兩個投影直方圖應該與例1中的對應
直方圖是一樣的