图像直方图是反映一个图像像素分布的统计表,其实横坐标代表了图像像素的种类,可以是灰度的,也可以是彩色的。纵坐标代表了每一种颜色值在图像中的像素总数或者占所有像素个数的百分比
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 const Mat* images:输入图像 int nimages:输入图像的个数 const int* channels:需要统计直方图的第几通道(0表示第一通道) InputArray mask:掩膜,控制运算的范围。不懂的可以看这篇博文:[图像位操作(带mask解说)](https://blog.csdn.net/m0_60447786/article/details/125650689?spm=1001.2014.3001.5501) OutputArray hist:输出的直方图数组 int dims:需要统计直方图通道的个数(一般都是1) const int* histSize:指的是直方图分成多少个区间,就是 bin的个数 const float** ranges: 统计像素值得区间(相当于每一个横坐标区间的取值范围) bool uniform=true::是否对得到的直方图数组进行归一化处理 bool accumulate=false:在多个图像时,是否累计计算像素值得个数 注意:后两个参数也可以了忽略,忽略的时候默认是 true false
calcHist函数只是计算直方图的数据,(直方图数据需要一个mat类型的变量来存储)
绘制直方图流程1.读入图像
2.分开图像通道(不一定只有三个通道)
3.定义直方图的参数
4.使用calcHist()函数得到直方图数据
5.定义幕布参数
6.归一化直方图参数
7.将直方图数据画在幕布上
8.显示幕布
源码如下:
#include最终效果#include #include using namespace std; using namespace cv; int main() { //绘制图像直方图 Mat src = imread("C:\Users\86151\Desktop\opencv\picture\4.jpg"); imshow("src",src); if (src.empty()) //这里建议大家加上,形成习惯,有时候会帮助我们及时发现bug,博主亲身经历 { cout << "no picture" << endl; return -1; } //提取三通道 vector all_channel; split(src,all_channel); //split函数将图像的三通道分别提取出来,放到all_channel数组里面 //定义参数变量 const int bin = 256;//因为图像一共有256个灰度级别,意思就是像素点0-255有256个,那么需要256个横坐标,所以bin赋值256。 float bin_range[2] = { 0,255 };//每个通道的灰度级别0-255,也就是像素点的取值范围 const float* ranges[1] = { bin_range };//这样做只是方便下面clacHist函数的传参 //定义变量来存储直方图数据,一共提取出了三个通道:R G B,所以定义三个变量 Mat b_hist; Mat g_hist; Mat r_hist; //计算得到直方图数据 calcHist(&all_channel[0], 1, 0, Mat(), b_hist, 1, &bin, ranges, true, false); calcHist(&all_channel[1], 1, 0, Mat(), g_hist, 1, &bin, ranges, true, false); calcHist(&all_channel[2], 1, 0, Mat(), r_hist, 1, &bin, ranges, true, false); //设置直方图画布的参数,直方图要花在一个“幕布”上,这个幕布也要设置参数 int hist_w = 512;//画布的宽 int hist_h = 400;//画布的高 int bin_w = cvRound((double)hist_w/bin);//设置每一点直方图的步长,直方图有256个横坐标,每个坐标在画布中占多长,通过hist_w/bin计算得出。cvRound()函数是“四舍五入”的作用。 Mat hist_canvas = Mat::zeros(hist_h, hist_w, CV_8UC3);//通过我们设置的参数创建出一个黑色的画布 //对三个通道的直方图数据进行归一化处理,这是一个必要环节,后续仔细说明。 normalize(b_hist, b_hist, 0, 255, NORM_MINMAX, -1, Mat()); normalize(g_hist, g_hist, 0, 255, NORM_MINMAX, -1, Mat()); normalize(r_hist, r_hist, 0, 255, NORM_MINMAX, -1, Mat()); //以折线统计图的方式绘制三个通道的直方图在画布上 for (int i = 1; i < 256; i++) { line(hist_canvas, Point(bin_w * (i-1), hist_h - cvRound(b_hist.at (i-1))), Point(bin_w * (i), hist_h - cvRound(b_hist.at (i))), Scalar(0, 0, 255), 2, 8, 0); line(hist_canvas, Point(bin_w * (i-1), hist_h - cvRound(g_hist.at (i-1))), Point(bin_w * (i), hist_h - cvRound(g_hist.at (i))), Scalar(0, 255, 0), 2, 8, 0); line(hist_canvas, Point(bin_w * (i-1), hist_h - cvRound(r_hist.at (i-1))), Point(bin_w * (i), hist_h - cvRound(r_hist.at (i))), Scalar(255, 0, 0), 2, 8, 0); } //展示到屏幕上 imshow("result",hist_canvas); waitKey(0); destroyAllWindows(); return 0; }
带有归一化的效果:
不带有归一化的效果:
由此可见,我们归一化的目的就是让数据同一在一个范围内,因为我们观察的就是各个像素值所占的比例,所以同时放缩到一个范围是不错的选择,易于观察。对normalize函数有疑问的也可以参考下这篇文章:normalize函数详解



