OpenCV直方图计算匹配方法详解calcHist

       这次再深入学习一下calcHist函数,即用于计算直方图的函数,主要是分析一下该函数的众多的参数,看看应该如何使用,先给出一段代码,其中包括两部分,一部分来自opencv_tutorials中的例子,一部分来自opencv2refman中,都进行了修改,opencv版本为2.3.1。
  1. #include <opencv2/core/core.hpp>  
  2. #include <opencv2/highgui/highgui.hpp>  
  3. #include <opencv2/imgproc/imgproc.hpp>  
  4. #include <iostream>  
  5.   
  6. #pragma comment(lib, "opencv_core231d.lib")  
  7. #pragma comment(lib, "opencv_highgui231d.lib")  
  8. #pragma comment(lib, "opencv_imgproc231d.lib")  
  9.   
  10. using namespace cv;  
  11. using namespace std;  
  12.   
  13. #define HIST_DIM1  
  14.   
  15. int main( int argc, char** argv )  
  16. {  
  17. #ifdef HIST_DIM1  
  18. //----------------------example 1-------------------------------//  
  19.     Mat src, dst;  
  20.     /// Load image  
  21.     src = imread("d:/picture/lena.jpg");  
  22.   
  23.     if( !src.data )  
  24.     {   
  25.         cout<<"load image failed"<<endl;  
  26.         return -1;   
  27.     }  
  28.   
  29.     /// Separate the image in 3 places ( R, G and B )  
  30.     vector<Mat> rgb_planes;  
  31. #define SHOW_HSV  
  32. #ifdef SHOW_HSV  
  33.     Mat hsv;  
  34.     cvtColor(src, hsv, COLOR_BGR2HSV);  
  35.     split(hsv, rgb_planes );   
  36. #else  
  37.     split(src, rgb_planes );   
  38. #endif  
  39.     /// Establish the number of bins   
  40.     int histSize = 256;  
  41.   
  42.     /// Set the ranges ( for R,G,B) )  
  43.     float range[] = { 0, 255 } ;   
  44.     const float* histRange = { range };  
  45.   
  46.     bool uniform = truebool accumulate = false;  
  47.   
  48.     Mat r_hist, g_hist, b_hist;  
  49.   
  50.     /// Compute the histograms:  
  51.     calcHist( &rgb_planes[2], 1, 0, Mat(), r_hist, 1, &histSize, &histRange, uniform, accumulate );  
  52.     calcHist( &rgb_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRange, uniform, accumulate );  
  53.     calcHist( &rgb_planes[0], 1, 0, Mat(), b_hist, 1, &histSize, &histRange, uniform, accumulate );  
  54.   
  55.     // Draw the histograms for R, G and B  
  56.     int hist_w = 600; int hist_h = 400;  
  57.     int bin_w = cvRound( (double) hist_w/histSize );  
  58.   
  59.     Mat rgb_hist[3];  
  60.     for(int i=0; i<3; ++i)  
  61.     {  
  62.         rgb_hist[i] = Mat(hist_h, hist_w, CV_8UC3, Scalar::all(0));  
  63.     }  
  64.       
  65.     Mat histImage(hist_h, hist_w, CV_8UC3, Scalar(0,0,0));  
  66.   
  67.     /// Normalize the result to [ 0, histImage.rows-10]  
  68.     normalize(r_hist, r_hist, 0, histImage.rows-10, NORM_MINMAX);  
  69.     normalize(g_hist, g_hist, 0, histImage.rows-10, NORM_MINMAX);  
  70.     normalize(b_hist, b_hist, 0, histImage.rows-10, NORM_MINMAX);  
  71.   
  72.     /// Draw for each channel   
  73.     forint i = 1; i < histSize; i++ )  
  74.     {   
  75.         line( histImage, Point( bin_w*(i-1), hist_h-cvRound(r_hist.at<float>(i-1)) ) ,   
  76.             Point( bin_w*(i), hist_h-cvRound(r_hist.at<float>(i)) ),   
  77.             Scalar( 0, 0, 255), 1);   
  78.         line( histImage, Point( bin_w*(i-1), hist_h - cvRound(g_hist.at<float>(i-1)) ) ,   
  79.             Point( bin_w*(i), hist_h-cvRound(g_hist.at<float>(i)) ),   
  80.             Scalar( 0, 255, 0), 1);   
  81.         line( histImage, Point( bin_w*(i-1), hist_h - cvRound(b_hist.at<float>(i-1)) ) ,   
  82.             Point( bin_w*(i), hist_h-cvRound(b_hist.at<float>(i)) ),   
  83.             Scalar( 255, 0, 0), 1);         
  84.     }  
  85.   
  86.     for (int j=0; j<histSize; ++j)  
  87.     {  
  88.         int val = saturate_cast<int>(r_hist.at<float>(j));  
  89.         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);  
  90.           
  91.         val = saturate_cast<int>(g_hist.at<float>(j));  
  92.         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);  
  93.           
  94.         val = saturate_cast<int>(b_hist.at<float>(j));  
  95.         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);  
  96.     }  
  97.   
  98.     /// Display   
  99.     namedWindow("calcHist Demo", CV_WINDOW_AUTOSIZE );  
  100.     namedWindow("wnd");  
  101.     imshow("calcHist Demo", histImage );  
  102.     imshow("wnd", src);  
  103.   
  104.     imshow("R", rgb_hist[0]);  
  105.     imshow("G", rgb_hist[1]);  
  106.     imshow("B", rgb_hist[2]);  
  107. #else  
  108. //----------------------example 2-------------------------------//  
  109.     Mat src, hsv;  
  110.     if(!(src=imread("d:/picture/lena.bmp")).data)  
  111.         return -1;  
  112.     cvtColor(src, hsv, CV_BGR2HSV);  
  113.     // Quantize the hue to 30 levels  
  114.     // and the saturation to 32 levels  
  115.     int hbins = 60, sbins = 64;  
  116.     int histSize[] = {hbins, sbins};  
  117.     // hue varies from 0 to 179, see cvtColor  
  118.     float hranges[] = { 0, 180 };  
  119.     // saturation varies from 0 (black-gray-white) to  
  120.     // 255 (pure spectrum color)  
  121.     float sranges[] = { 0, 256};  
  122.     const float*ranges[] = { hranges, sranges };  
  123.     MatND hist;  
  124.     // we compute the histogram from the 0-th and 1-st channels  
  125.     int channels[] = {0, 1};  
  126.     calcHist( &hsv, 1, channels, Mat(),hist, 2, histSize, ranges,truefalse );  
  127.     double maxVal=0;  
  128.     minMaxLoc(hist, 0, &maxVal, 0, 0);  
  129.     int scale = 8;  
  130.     Mat histImg = Mat::zeros(sbins*scale, hbins*scale, CV_8UC3);  
  131.     forint h = 0; h < hbins; h++ )  
  132.     {  
  133.         forint s = 0; s < sbins; s++ )  
  134.         {  
  135.             float binVal = hist.at<float>(h, s);  
  136.             int intensity = cvRound(binVal*255/maxVal);  
  137.             rectangle( histImg, Point(h*scale, s*scale),Point((h+1)*scale-1, (s+1)*scale-1), Scalar::all(intensity), CV_FILLED);  
  138.         }  
  139.     }  
  140.     namedWindow( "Source", 1 );  
  141.     imshow( "Source", src );  
  142.     namedWindow( "H-S Histogram", 1 );  
  143.     imshow( "H-S Histogram", histImg );  
  144. #endif    
  145. //-------------------------------------------------------------------------//     
  146.     waitKey(0);  
  147.     destroyAllWindows();  
  148.     return 0;  
  149. }  


上面的例子是对opencv_tutorials以及手册中的计算直方图的程序的修改

其中修改的:
1、原先的程序中对加载的彩色rgb图像的通道有问题(看例子给的图应该是在linux下的,不知道是不是因为linux和windows下加载的不同),在windows下默认加载的通道排列顺序是B-G-R,
原先的程序中是按照R-G-B顺序计算的直方图所以需要变换一下顺序;
2、原先程序的histImage将参数顺序弄错了,该构造函数的第一个参数是rows行数,对应图像的高度,即hist_h,而不是hist_w,这里同时还将大小变换了一下
看着更舒服一些;
下面是对calcHist函数的参数介绍。

calcHist--计算矩阵的直方图函数;
--------------------------------------------------------------------------------------------------------------

  1. ###---given in manual---###  
  2. void calcHist(const Mat*arrays, int narrays, const int* channels, InputArray mask, OutputArray hist, int dims,  
  3.     const int* histSize, const float** ranges, bool uniform=true, boolaccumulate=false)  
  4. void calcHist(const Mat*arrays, int narrays, const int* channels, InputArray mask, SparseMat& hist, int dims,  
  5.     const int* histSize, const float** ranges, bool uniform=true, boolaccumulate=false)  
  6.   
  7. ###---declaration in imgproc.hpp---###  
  8. //! computes the joint dense histogram for a set of images.  
  9. CV_EXPORTS void calcHist( const Mat* images, int nimages, const int* channels, InputArray mask, OutputArray hist, int dims,  
  10.       const int* histSize, const float** ranges, bool uniform=truebool accumulate=false );  
  11.   
  12. //! computes the joint sparse histogram for a set of images.  
  13. CV_EXPORTS void calcHist( const Mat* images, int nimages, const int* channels, InputArray mask, SparseMat& hist, int dims,  
  14.       const int* histSize, const float** ranges, bool uniform=truebool accumulate=false );  
  15.   
  16. CV_EXPORTS_W void calcHist( InputArrayOfArrays images, const vector<int>& channels, InputArray mask, OutputArray hist,  
  17.        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中的对应
直方图是一样的。


上次对calcHist的参数进行了分析,并且给出了几个例子,但是对channels参数没搞清楚,今天又写了个例子分析了一下,终于弄明白了。

         calcHist函数的channels参数和narrays以及dims共同来确定用于计算直方图的图像;
首先dims是最终的直方图维数,narrays指出了arrays数组中图像的个数,其中每一幅图像都可以是任意通道的【只要最终dims不超过32即可】

如果channels参数为0,则narrays和dims必须相等,否则弹出assert,此时计算直方图的时候取数组中每幅图像的第0通道。
当channels不是0的时候,用于计算直方图的图像是arrays中由channels指定的通道的图像,channels与arrays中的图像的对应关系,如channels的参数说明的,将arrays中的图像从第0幅开始按照通道摊开排列起来,然后channels中的指定的用于计算直方图的就是这些摊开的通道;
假设有arrays中只有一幅三通道的图像image,那么narrays应该为1,如果是想计算3维直方图【最大也只能是3维的】,想将image的通道2作为第一维,通道0作为第二维,通道1作为第三维,则可以将channels设置为channesl={2,0,1};这样calcHist函数计算时就按照这个顺序来统计直方图。
可以看出channels不为0时narrays可以和dims不相等,只要保证arrays中至少有channels指定的通道就可以。

下面是例子和一个运行的图像,还是使用lena图像;

  1.     Mat src, hsv;  
  2.     if(!(src=imread("d:/picture/lena.bmp")).data)  
  3.         return -1;  
  4.     cvtColor(src, hsv, CV_BGR2HSV);  
  5.     vector<Mat> hsv_plane;  
  6.     split(hsv, hsv_plane);  
  7.     Mat inputs[]={hsv_plane[1], hsv_plane[2], hsv_plane[0]};  
  8.       
  9.     vector<Mat> mixmat_plane;  
  10.     mixmat_plane.push_back(hsv_plane[2]);  
  11.     mixmat_plane.push_back(hsv_plane[0]);  
  12.     Mat mixmat;  
  13.     merge(mixmat_plane, mixmat);  
  14.     Mat mixed[]={mixmat,hsv_plane[1]};  
  15.   
  16.     int vbins = 128, sbins = 128, hbins = 128;  
  17.     int histSize[] = {sbins, vbins, hbins};  
  18.     float sranges[] = { 0, 256};  
  19.     float vranges[] = { 0, 256};  
  20.     float hranges[] = { 0, 256};  
  21.     const float*ranges[] = {sranges, vranges, hranges};  
  22.     MatND hist;  
  23.   
  24. //#define SINGLE_MAT  
  25. #define MIX_MAT  
  26.   
  27. #ifdef SINGLE_MAT     
  28. /* 
  29. use one multi-channel mat, channels param gives the channels used;  
  30. 使用多通道的图像计算多维直方图,可以计算1,2,3维的; 
  31. */  int channels[] = {1, 2};  
  32.     calcHist(&hsv, 1, channels, Mat(),hist, 2, histSize, ranges,truefalse );  
  33. #elif defined MIX_MAT   
  34. /* 
  35. use mix mat array, the first elem is a single channel mat, second is a two channel mat; 
  36. 使用混合通道图像数组,第1个图像是2通道的,第2个是单通道的; 
  37. channels指定每一维对应的通道; 
  38. */    
  39.     int channels[] = {1, 2, 0};  
  40.   
  41. // #define DIM_2  
  42. #ifdef DIM_2  
  43. //统计二维直方图;  
  44.     calcHist(mixed, 2, channels, Mat(),hist, 2, histSize, ranges,truefalse);  
  45. #else  
  46. //统计三维直方图;  
  47.     calcHist(mixed, 2, channels, Mat(),hist, 3, histSize, ranges,truefalse);  
  48. #endif  
  49.   
  50. #else  
  51. /* 
  52. use multi-mat arrays, channels param gives the array mat and its channels used; 
  53. 使用都是单通道图像数组计算2维直方图--也可以计算3维的; 
  54. */    
  55.     int channels[] = {2, 1};  
  56.     hbins = 1;  
  57.     calcHist(inputs, 3, channels, Mat(),hist, 2, histSize, ranges,truefalse );  
  58. #endif  
  59.   
  60. #ifndef MIX_MAT  
  61.     double maxVal=0;  
  62.     minMaxLoc(hist, 0, 0, 0, 0);//only can process mat that dims<=2--minMaxLoc只能处理2维以下的;  
  63. #endif  
  64.   
  65.     int scale = 4;  
  66.     Mat histImg = Mat::zeros(vbins*scale, sbins*scale, CV_8UC3);  
  67.     float *hist_sta = new float[sbins];  
  68.     float *hist_val = new float[vbins];  
  69.     float *hist_hue = new float[hbins];  
  70.     memset(hist_val, 0, vbins*sizeof(float));  
  71.     memset(hist_sta, 0, sbins*sizeof(float));  
  72.     memset(hist_hue, 0, hbins*sizeof(float));  
  73.   
  74.     forint s = 0; s < sbins; s++ )  
  75.     {  
  76.         forint v = 0; v < vbins; v++ )  
  77.         {  
  78.             for(int h=0; h<hbins; h++)  
  79.             {  
  80. #ifdef MIX_MAT  
  81. //-----------------------------------------------------------//  
  82. #ifdef DIM_2  
  83.                 float binVal = hist.at<float>(s, v);  
  84. #else  
  85.                 float binVal = hist.at<float>(s, v, h);  
  86.                 hist_hue[h] += binVal;  
  87. #endif  
  88. //-----------------------------------------------------------//               
  89. #else  
  90.                 float binVal = hist.at<float>(s, v);  
  91.                 int intensity = cvRound(binVal*255/maxVal);  
  92.                 rectangle( histImg, Point(s*scale, v*scale),Point((s+1)*scale-1, (v+1)*scale-1), Scalar::all(intensity), CV_FILLED);  
  93. #endif  
  94.                 hist_val[v] += binVal;  
  95.                 hist_sta[s] += binVal;  
  96.   
  97.             }  
  98.         }  
  99.     }  
  100.     //find max bin value;  
  101.     double max_sta=.0, max_val=.0,max_hue=.0;  
  102.     for(int i=0; i<sbins; ++i)  
  103.     {  
  104.         if(hist_sta[i]>max_sta)  
  105.             max_sta = hist_sta[i];  
  106.     }  
  107.     for(int i=0; i<vbins; ++i)  
  108.     {  
  109.         if(hist_val[i]>max_val)  
  110.             max_val = hist_val[i];  
  111.     }  
  112.     for(int i=0; i<hbins; ++i)  
  113.     {  
  114.         if(hist_hue[i]>max_hue)  
  115.             max_hue = hist_hue[i];  
  116.     }  
  117.   
  118.     Mat sta_img = Mat::zeros(310, sbins*scale+20, CV_8UC3);  
  119.     Mat val_img = Mat::zeros(310, vbins*scale+20, CV_8UC3);  
  120.     Mat hue_img = Mat::zeros(310, hbins*scale+20, CV_8UC3);  
  121.   
  122.     for(int i=0; i<sbins; ++i)  
  123.     {  
  124.         int intensity = cvRound(hist_sta[i]*(sta_img.rows-10)/max_sta);  
  125.         rectangle(sta_img, Point(i*scale+10, sta_img.rows-intensity),Point((i+1)*scale-1+10, sta_img.rows-1), Scalar(0,255,0), 1);  
  126.     }  
  127.     for(int i=0; i<vbins; ++i)  
  128.     {  
  129.         int intensity = cvRound(hist_val[i]*(val_img.rows-10)/max_val);  
  130.         rectangle(val_img, Point(i*scale+10, val_img.rows-intensity),Point((i+1)*scale-1+10, val_img.rows-1), Scalar(0,0,255), 1);  
  131.     }  
  132.     for(int i=0; i<hbins; ++i)  
  133.     {  
  134.         int intensity = cvRound(hist_hue[i]*(hue_img.rows-10)/max_hue);  
  135.         rectangle(hue_img, Point(i*scale+10, hue_img.rows-intensity),Point((i+1)*scale-1+10, hue_img.rows-1), Scalar(255,0,0), 1);  
  136.     }  
  137.   
  138.     namedWindow( "Source");  
  139.     imshow( "Source", src );  
  140.     namedWindow( "Histogram");  
  141.     imshow( "Histogram", histImg );  
  142.     namedWindow("dim1");  
  143.     imshow("dim1", sta_img);  
  144.     namedWindow("dim2");  
  145.     imshow("dim2", val_img);      
  146.     namedWindow("dim3");  
  147.     imshow("dim3", hue_img);  


 

 

程序中使用了一些宏来控制不同的情况,比较简单一看就明白,毋庸多说。上图中的channels顺序是1,2,0,而图像数组是采用将VH组成一个两通道图像以及S图像放到一个数组中,即混合通道的数组,计算的是3通道的直方图,然后将每一维拆开了,分别显示在dim1-3中。
颜色直方图距离匹配:
方法一,三个通道分别计算直方图然后归一化合成一维向量,计算距离
方法二,三通道直方图合成一维向量,然后归一化,最后计算距离,
方法二的效果要明显好于方法一,略逊于直接计算二维直方图。



  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值