直方图
简单的说,直方图就是对数据进行统计,将统计值组织到一系列的事先定义好的的 bin 中, bin 的数据是从数据中计算出的特征统计量,这些数据可以是梯度,方向,色彩,或者其它特征。无论如何,直方图获得的是数据分布的统计图。我们最常见的是灰度直方图,也就是统计一副图片中,灰度值的分布情况。
假设有如下的矩阵包含一张8位的灰度图像(0-255)
我们可以对矩阵中的灰度值分布进行统计。首先,我们需要把0-255分成 17 个 区域(bin),如下图所示:
我们对每个范围中的灰度值进行统计排序,做出如下的表格:
我们是以图像的灰度为例子说明这个直方图,当然直方图不仅仅用于灰度特种统计排序,还可以用于图像的梯度、方向等特征。
在以上的过程中,我们使用到一些重要的参数,理解这些参数帮助我们更好的使用API函数。
dims:需要统计的特征的数目,我们上面只统计了 灰度值这个特征,所以, dims =1;
bin :每个特征子区段的数目,上面我们是分成17 个子区段,每个是16个数目,所以,bin =16;
range:每个特征的范围,在上面的例子中,range = [0, 255];
对于多维的情况,我们下边会举例说明,我们先从一维的开始说起。
直方图计算 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 );
绘制图像的一维直方图
int main()
{
//Load and show image
Mat src;
src = imread("D:/Lena.jpg");
if (!src.data)
{
cout<<"Picture loading failed !"<<endl;
return -1;
}
//Transform gray scale image
cvtColor(src,src,COLOR_BGR2GRAY);
// define parameter
int channels = 0;
Mat histImage;
int dims =1;
float hrange[] = {0,255};
const float *range[] = {hrange};
int size = 256;
calcHist(&src,1,&channels,Mat(),histImage,dims,&size,range,true,false);
Mat dstImage(size,size,CV_8UC3,Scalar(0,0,0));
// get minimum
double maxValue =0;
minMaxLoc(histImage,0,&maxValue,0,0);
for (int i=1;i<size;i++)
{
float binValue = histImage.at<float>(i);
//直方图的背景图像的高度是256,但是图像显示一般是在北京的中间比较好,
//所以把图像的像素归一化为 cvRound(250*0.9) = 230;
//这样,最大灰度值得统计数目也在就是在(256-230)位置,在图像的中心上下,
int value = cvRound(binValue*256*0.9/maxValue);
rectangle(dstImage,Point((i-1),size-1),Point(i, size-value),Scalar(255));
}
namedWindow("Win2");
imshow("Win2",dstImage);
waitKey(0);
return 0;
}
绘制图像的H-S直方图
绘制图像的RGB通道直方图
int main()
{
//------------------------读取图像------------------------//
Mat src, dst;
src = imread("D:/mogu.jpg");
if (!src.data)
{
cout<<"Picture loading failed !"<<endl;
return -1;
}
namedWindow("Win1");
imshow("Win1",src);
//------------------------图像通道的分隔------------------------//
vector<Mat> rgb_channel;
split(src, rgb_channel);
//------------------------计算直方图----------------------------//
int histsize =256;
float range[] = {0,256};
const float* histRange = {range};
Mat r_Hist,g_Hist,b_Hist;
calcHist(&rgb_channel[0],1,0,Mat(),b_Hist,1,&histsize,&histRange,true,false);
calcHist(&rgb_channel[1],1,0,Mat(),g_Hist,1,&histsize,&histRange,true,false);
calcHist(&rgb_channel[2],1,0,Mat(),r_Hist,1,&histsize,&histRange,true,false);
namedWindow("Win3");
imshow("Win3",b_Hist);
//------------------------归一化----------------------------//
int hist_h = 400;
int hist_w = 768;
int bin_w =hist_w / histsize;
normalize(b_Hist,b_Hist,0,hist_h,NORM_MINMAX,-1,Mat());
normalize(g_Hist,g_Hist,0,hist_h,NORM_MINMAX,-1,Mat());
normalize(r_Hist,r_Hist,0,hist_h,NORM_MINMAX,-1,Mat());
//------------------------画直方图----------------------------//
Mat histImage(hist_h,hist_w,CV_8UC3,Scalar(0,0,0));
for (int i = 1; i<histsize; i++)
{
// draw rectangle
rectangle(histImage, Point(((i-1)*bin_w),hist_h),Point((i*bin_w),hist_h - cvRound(b_Hist.at<float>(i))), Scalar(255,0,0));
rectangle(histImage, Point(((i-1)*bin_w),hist_h),Point((i*bin_w),hist_h - cvRound(g_Hist.at<float>(i))), Scalar(0,255,0));
rectangle(histImage, Point(((i-1)*bin_w),hist_h),Point((i*bin_w),hist_h - cvRound(r_Hist.at<float>(i))), Scalar(0,0,255));
}
namedWindow("Win2");
imshow("Win2",histImage);
waitKey(0);
return 0;
}