上次对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图像;
Mat src, hsv;
if(!(src=imread("d:/picture/lena.bmp")).data)
return -1;
cvtColor(src, hsv, CV_BGR2HSV);
vector<Mat> hsv_plane;
split(hsv, hsv_plane);
Mat inputs[]={hsv_plane[1], hsv_plane[2], hsv_plane[0]};
vector<Mat> mixmat_plane;
mixmat_plane.push_back(hsv_plane[2]);
mixmat_plane.push_back(hsv_plane[0]);
Mat mixmat;
merge(mixmat_plane, mixmat);
Mat mixed[]={mixmat,hsv_plane[1]};
int vbins = 128, sbins = 128, hbins = 128;
int histSize[] = {sbins, vbins, hbins};
float sranges[] = { 0, 256};
float vranges[] = { 0, 256};
float hranges[] = { 0, 256};
const float*ranges[] = {sranges, vranges, hranges};
MatND hist;
//#define SINGLE_MAT
#define MIX_MAT
#ifdef SINGLE_MAT
/*
use one multi-channel mat, channels param gives the channels used;
使用多通道的图像计算多维直方图,可以计算1,2,3维的;
*/ int channels[] = {1, 2};
calcHist(&hsv, 1, channels, Mat(),hist, 2, histSize, ranges,true, false );
#elif defined MIX_MAT
/*
use mix mat array, the first elem is a single channel mat, second is a two channel mat;
使用混合通道图像数组,第1个图像是2通道的,第2个是单通道的;
channels指定每一维对应的通道;
*/
int channels[] = {1, 2, 0};
// #define DIM_2
#ifdef DIM_2
//统计二维直方图;
calcHist(mixed, 2, channels, Mat(),hist, 2, histSize, ranges,true, false);
#else
//统计三维直方图;
calcHist(mixed, 2, channels, Mat(),hist, 3, histSize, ranges,true, false);
#endif
#else
/*
use multi-mat arrays, channels param gives the array mat and its channels used;
使用都是单通道图像数组计算2维直方图--也可以计算3维的;
*/
int channels[] = {2, 1};
hbins = 1;
calcHist(inputs, 3, channels, Mat(),hist, 2, histSize, ranges,true, false );
#endif
#ifndef MIX_MAT
double maxVal=0;
minMaxLoc(hist, 0, 0, 0, 0);//only can process mat that dims<=2--minMaxLoc只能处理2维以下的;
#endif
int scale = 4;
Mat histImg = Mat::zeros(vbins*scale, sbins*scale, CV_8UC3);
float *hist_sta = new float[sbins];
float *hist_val = new float[vbins];
float *hist_hue = new float[hbins];
memset(hist_val, 0, vbins*sizeof(float));
memset(hist_sta, 0, sbins*sizeof(float));
memset(hist_hue, 0, hbins*sizeof(float));
for( int s = 0; s < sbins; s++ )
{
for( int v = 0; v < vbins; v++ )
{
for(int h=0; h<hbins; h++)
{
#ifdef MIX_MAT
//-----------------------------------------------------------//
#ifdef DIM_2
float binVal = hist.at<float>(s, v);
#else
float binVal = hist.at<float>(s, v, h);
hist_hue[h] += binVal;
#endif
//-----------------------------------------------------------//
#else
float binVal = hist.at<float>(s, v);
int intensity = cvRound(binVal*255/maxVal);
rectangle( histImg, Point(s*scale, v*scale),Point((s+1)*scale-1, (v+1)*scale-1), Scalar::all(intensity), CV_FILLED);
#endif
hist_val[v] += binVal;
hist_sta[s] += binVal;
}
}
}
//find max bin value;
double max_sta=.0, max_val=.0,max_hue=.0;
for(int i=0; i<sbins; ++i)
{
if(hist_sta[i]>max_sta)
max_sta = hist_sta[i];
}
for(int i=0; i<vbins; ++i)
{
if(hist_val[i]>max_val)
max_val = hist_val[i];
}
for(int i=0; i<hbins; ++i)
{
if(hist_hue[i]>max_hue)
max_hue = hist_hue[i];
}
Mat sta_img = Mat::zeros(310, sbins*scale+20, CV_8UC3);
Mat val_img = Mat::zeros(310, vbins*scale+20, CV_8UC3);
Mat hue_img = Mat::zeros(310, hbins*scale+20, CV_8UC3);
for(int i=0; i<sbins; ++i)
{
int intensity = cvRound(hist_sta[i]*(sta_img.rows-10)/max_sta);
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);
}
for(int i=0; i<vbins; ++i)
{
int intensity = cvRound(hist_val[i]*(val_img.rows-10)/max_val);
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);
}
for(int i=0; i<hbins; ++i)
{
int intensity = cvRound(hist_hue[i]*(hue_img.rows-10)/max_hue);
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);
}
namedWindow( "Source");
imshow( "Source", src );
namedWindow( "Histogram");
imshow( "Histogram", histImg );
namedWindow("dim1");
imshow("dim1", sta_img);
namedWindow("dim2");
imshow("dim2", val_img);
namedWindow("dim3");
imshow("dim3", hue_img);
程序中使用了一些宏来控制不同的情况,比较简单一看就明白,毋庸多说。上图中的channels顺序是1,2,0,而图像数组是采用将VH组成一个两通道图像以及S图像放到一个数组中,即混合通道的数组,计算的是3通道的直方图,然后将每一维拆开了,分别显示在dim1-3中。