OpenCV之直方图绘制(calcHist函数详解)

1.直方图的定义

要理解直方图,绕不开“亮度”这个概念。人们把亮度分为0到255共256个数值,数值越大,代表的亮度越高。其中0代表纯黑色的最暗区域,255表示最亮的纯白色,而中间的数字就是不同亮度的灰色。人们还进一步把这些亮度分为了5个区域,分别是黑色,阴影,中间调,高光和白色。
在这里插入图片描述
在图像处理上,直方图是图像信息统计的有力工具。其实也就是统计一幅图某个亮度像素数量。

2.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 );

参数解释:
• images:输入的图像的指针;
• nimages:输入图像个数;
• channels:需要统计直方图的第几通道;
• mask:掩模,mask必须是一个8位(CV_8U)的数组并且和images的数组大小相同;
• hist:直方图计算的输出值;
• dims:输出直方图的维度(由channels指定);
• histSize:直方图中每个dims维度需要分成多少个区间(如果把直方图看作一个一个竖条的话,就是竖条的个数);
• ranges:统计像素值的区间;
• uniform=true:是否对得到的直方图数组进行归一化处理;
• accumulate=false:在多个图像时,是否累积计算像素值的个数;

【注】:在计算图像直方图的时候一般配合minMaxLoc()和normalize()函数一起使用。

3.绘制直方图

只看函数说明肯定看不明白,下面来对函数进行代码演示。绘制直方图主要有三步:

3.1 读取原图像并检查图像是否读取成功

相关代码如下:

Mat src, gray;
src = imread("D:/Desktop/16.png");
if (!src.data)
{
	cout << "读取图片错误,请重新输入正确路径!\n";
	system("pause");
	return -1;
}
cvtColor(src, gray, CV_BGR2GRAY);//转换为灰度图像
imshow("【灰度图】", gray);

当然,如果觉得麻烦,可以直接用以下代码代替:

Mat src, gray;
grayImage = imread("D:/Desktop/16.png",0);
imshow("【灰度图】", gray);

3.2 定义直方图参数并计算直方图

//需要计算的图像的通道,灰度图像为0,BGR图像需要指定B,G,R
	const int channels[] = { 0 };
	Mat hist;//定义输出Mat类型
	int dims = 1;//设置直方图维度
	const int histSize[] = { 256 }; //直方图每一个维度划分的柱条的数目
	//每一个维度取值范围
	float pranges[] = { 0, 255 };//取值区间
	const float* ranges[] = { pranges };
	
	calcHist(&gray, 1, channels, Mat(), hist, dims, histSize, ranges, true, false);//计算直方图

3.3 绘制直方图

方法一:

	int scale = 2;
	int hist_height = 256;
	Mat hist_img = Mat::zeros(hist_height, 256 * scale, CV_8UC3); //创建一个黑底的8位的3通道图像,高256,宽256*2
	double max_val;
	minMaxLoc(hist, 0, &max_val, 0, 0);//计算直方图的最大像素值
	//将像素的个数整合到 图像的最大范围内
	//遍历直方图得到的数据
	for (int i = 0; i < 256; i++)
	{
		float bin_val = hist.at<float>(i);   //遍历hist元素(注意hist中是float类型)
		int intensity = cvRound(bin_val*hist_height / max_val);  //绘制高度
		rectangle(hist_img, Point(i*scale, hist_height - 1), Point((i + 1)*scale - 1, hist_height - intensity), Scalar(255, 255, 255));//绘制直方图
	}

输出结果:
在这里插入图片描述

方法二:

int hist_w = 500;
int hist_h = 300;
int nHistSize = 256;
int bin_w = cvRound((double)hist_w / nHistSize);	//区间
Mat histImage(hist_h, hist_w, CV_8UC3, Scalar(0, 0, 0));//创建一个黑底的8位的3通道图像,高300,宽500
normalize(hist, hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());//将直方图归一化到[0,histImage.rows]
for (int i = 1; i < nHistSize; i++)
{
	line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(hist.at<float>(i - 1))),Point(bin_w*(i), hist_h - cvRound(hist.at<float>(i))), Scalar(255, 0, 0));
}

输出结果:
在这里插入图片描述
ok,以上便是OpenCV绘制灰度直方图的全部内容了,自行天剑opencv库即可运行全程序,如果实在无法独立完成,可以参考资源:
https://download.csdn.net/download/didi_ya/14989048

4.关于BGR直方图的绘制

上面介绍了灰度直方图的绘制,下面介绍一下BGR彩色直方图的绘制过程。
关于为什么叫BGR而不叫RGB,网上有许多误导性的博客,相信大家一定也有所了解,这里就不过多介绍了。

4.1 读取原图像并检查图像是否读取成功

同上

4.2 分通道显示

利用split函数进行分通道显示:

vector<Mat>bgr_planes;
split(src, bgr_planes);

4.3 分B,G,R计算直方图

int histSize = 256;
float range[] = { 0,255 };
const float*Ranges = { range };
Mat b_hist, g_hist, r_hist;
calcHist(&bgr_planes[0], 1, 0, Mat(), b_hist, 1, &histSize, &Ranges, true, false);
calcHist(&bgr_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, &Ranges, true, false);
calcHist(&bgr_planes[2], 1, 0, Mat(), r_hist, 1, &histSize, &Ranges, true, false);

【注意】:这里说明一下,第一行有两种写法,如果按照3.2所写:const int histSize[] = { 256 }; ,则后面计算直方图时直接写histSize即可,如果按照4.3所写:int histSize = 256;,那么下面调用计算直方图的calcHist()函数的时候,该变量需要写 &histSize
选择一种自己喜欢的方法即可,切记不要用混了!

4.4 绘制直方图

	//归一化
	int hist_w = 500;//直方图的图像的宽
	int hist_h = 300; //直方图的图像的高
	int nHistSize = 256;
	int bin_w = cvRound((double)hist_w / nHistSize);	//区间
	Mat histImage(hist_h, hist_w, CV_8UC3, Scalar(0, 0, 0));//绘制直方图显示的图像
	normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());//归一化
	normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());
	normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());
	for (int i = 1; i < nHistSize; i++)
	{
		//绘制蓝色分量直方图
		line(histImage, Point((i - 1)*bin_w, hist_h - cvRound(b_hist.at<float>(i - 1))),
			Point((i)*bin_w, hist_h - cvRound(b_hist.at<float>(i))), Scalar(255, 0, 0),2);
		//绘制绿色分量直方图
		line(histImage, Point((i - 1)*bin_w, hist_h - cvRound(g_hist.at<float>(i - 1))),
			Point((i)*bin_w, hist_h - cvRound(g_hist.at<float>(i))), Scalar(0, 255, 0),2);
		//绘制红色分量直方图
		line(histImage, Point((i - 1)*bin_w, hist_h - cvRound(r_hist.at<float>(i - 1))),
			Point((i)*bin_w, hist_h - cvRound(r_hist.at<float>(i))), Scalar(0, 0, 255),2);
	}

输出结果:
在这里插入图片描述

完整代码也可参考:
https://download.csdn.net/download/didi_ya/14989174

  • 40
    点赞
  • 258
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wendy_ya

您的鼓励将是我创作的最大动力~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值