对OpenCV的函数calcHist()进行透彻解析,并利用它实例绘制一个一维灰度直方图
Posted 昊虹算法
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了对OpenCV的函数calcHist()进行透彻解析,并利用它实例绘制一个一维灰度直方图相关的知识,希望对你有一定的参考价值。
图像直方图是图像处理中非常重要的像素统计结果,反映了图像像素点的概率分布情况。
图像直方图不再表征图像的纹理信息,而是对图像像素的统计。
由于同一物体无论是旋转还是平移在图像中都具有相同的灰度值,因此直方图具有平移不变性、放缩不变性等优点,因此可以用来查看图像整体的变化形式,例如图像是否过暗、图像像素灰度值主要集中在哪些范围等,在特定的条件下也可以利用图像直方图进行图像的识别,例如对数字的识别。
图像直方图还广泛应用于空间域处理、特征描述及特征匹配等领域。
图像直方图简单来说就是统计图像中每个灰度值的个数,之后将图像灰度值作为横轴,以灰度值个数或者灰度值所占比率作为纵轴绘制的统计图。
通过直方图可以看出图像中哪些灰度值数目较多,哪些较少。
在OpenCV 提供了图像直方图的统计函数calcHist(),该函数能够统计出图像中每个灰度值的个数,根据这个函数的统计结果我们可以绘制图像的一维直方图或多维直方图。
所以咱们先介绍直方图统计函数calcHist()的使用吧。
函数calcHist()的原型如下:
void cv::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---待统计直方图的图像数组,是一个MAT类型的数组,数组中所有的图像应具有相同的尺寸和数据类型,并且数据类型只能是CV_8U、CV_16U和CV_32F三种中的一种,但是不同图像的通道数可以不同。
nimages---待统计直方图的图像数量。
channels---需要参与形成多维直方图的通道索引数组,第一个图像的通道索引从0到images[0].channels()-1,第二个图像通道索引从images[0].channels()到images[0].channels()+ images[1].channels()-1,以此类推。举个例子,假如有三张图像,都是三通道。那么第一张图像三个通道的索引值分别为0、1、2;第二张图像的三个通道的索引值分别为3、4、5;第三张图像的三个通道的索引值分别为6、7、8。
现在我们要计算一个二维直方图,参与计算的通道为第一张图像的第一通道和第三张图像的第三通道,那么数组channels的定义和初始化如下:
int channels[2]=0,8
现在我们要计算一个三维直方图,参与计算的通道为第一张图像的第一通道、第二张图像的第二通道、第三张图像的第三通道。那么数组channels的定义和初始化如下:
int channels[3]=0,4,8
请注意:这里计算的是多维直方图,而不是每个通道单独的一维直方图,如果需要计算每个通道单独的一维直方图,则需要反复调用函数calcHist(),并且每次调用时数组channels的大小都只能为1。
mask---可选的操作掩码,如果是空矩阵则表示图像中所有位置的像素都计入直方图中,如果矩阵不为空,则必须与输入图像尺寸相同且数据类型为CV_8U。当不为空的时候,那些掩码值不为0的掩码对应的像素被纳入统计范围,而那些掩码值为0的掩码对应的像素则不被纳入统计。
hist---存储直方图统计结果,是一个dims维度的数组。问dims是什么东西?即下一个要介绍的参数。
dims---需要计算的直方图的维度,必须是整数,并且不能大于CV_MAX_DIMS,在OpenCV3和OpenCV 4.0中都为32。通常来说数组channels的大小为多少,这个值就写为多少。当然如果你有三个通道,也可以将其值写为1或2,经博主的实测,当写为1的时候计算的是通道索引数组中的第1个通道的直方图,当写为2的时候计算的是通道索引数组中的第1个通道和第2个通道的二维直方图。什么叫二维直方图,后边我会写篇博文专门介绍,等写好了我会把链接放到这里。
histSize---每个维度的直方图尺寸。它具体的作用我举个例子来说。当采用均匀划分时(即后面要介绍的参数uniform = true时),如果第1维度histSize的值为3,则代表用3个数来表示第1个维度的灰度直方图统计结果,如果其灰度级范围为0~2,则刚好一个数代表一个灰度级的个数。如果其灰度范围为0~4,则灰度范围被三个数平均划分为三个区间或者你也可以说是两个区间,第1个区间为[0,2),第2个区间为[2,4),第3个区间为[4,4)【大家看这第三个区间算区间么?可以说算也以说不算,所以刚才博主说灰度范围可以说被划分成了三个区间也可以说是划分成了两个区间】。再说具体点,计算结果hist中的第1个数表示灰度级为0和1的像素点个数;第2个数表示灰度级为2和3的像素点个数;第3个数按道理是表示灰度级为4的像素点个数,但这里,函数calcHist()不会去统计灰度级为4的像素点个数,原因和解决方法见我的另一篇博文:https://blog.csdn.net/wenhao_ir/article/details/124235212
ranges---每个维度的灰度值取值范围。
uniform--是否对灰度值进行均匀划分的标志。当采用均匀分划时,即uniform=ture时,显然我们只需知道每个维度的直方图尺寸和其灰度值范围我们便可以通过均匀划分确定出每个统计子区间的范围,此时ranges只需要两个数就行了,一个数表示灰度值范围的左端,一个数表示灰度值范围的右端。但是如果我们对灰度值不采用均匀划分,即uniform=false,那我们的函数calcHist()便不知道每个统计子区间的范围,这时候我们就需手动指定每个区间的范围,此时就要求ranges[i]中的元素个数为histSize[i]+1。举个例子,假如histSize[0]=3,那么就是说有三个统计区间,三个统计区间需要的边界点个数为3+1=4,即histSize[0]+1=3+1=4。
accumulate---累加标志。关于这个参数,我专门写了博文来介绍它,链接 https://blog.csdn.net/wenhao_ir/article/details/51140843
到这里,终于把函数calcHist()介绍完了,说实话,这个函数calcHist()的参数意义如果你没实际用过函数calcHist()几次,并且没把什么叫一维直方图,什么叫二维直方图的概念搞清楚的话,是不太好理解的。如果上面的介绍你有看不懂的地方,可以加博主的微信/QQ 2487872782与博主交流。
下面是利用函数calcHist绘制一维直方图的例子:
//博主微信/QQ 2487872782
//有问题可以联系博主交流
//有图像处理需求也可联系博主
//图像处理技术交流QQ群 271891601
//OpenCV版本:3.0
//VS版本:2012
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include<opencv2/imgcodecs/imgcodecs.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
Mat img = imread("F:/material/images/P0005-BaoXiaofeng.jpg");
if (img.empty())
cout << "Error: Could not load image" << endl;
return 0;
Mat srcImage;
cvtColor(img, srcImage, CV_BGR2GRAY);
imshow("【原图的灰度图】", srcImage);
//以下几行把calcHist()计算需要的参数配置好,
//具体各参数的意义我在博文中已经说得很详细了,
//这里就不多说了
int channels = 0;
Mat dstHist;
int histSize[] = 256 ;
float midRanges[] = 0, 256 ;
const float *ranges[] = midRanges ;
//参数配置好后,调用calcHis函数计算直方图数据
calcHist(&srcImage, 1, &channels, Mat(), dstHist, 1, histSize, ranges, true, false);
//calcHist函数调用结束后,dstHist变量中将储存绘制直方图需要的数据
//接下来绘制直方图
//首先先创建一个黑底的图像,为了可以显示彩色,所以该绘制图像是一个8位的3通道图像
Mat drawImage = Mat::zeros(Size(256, 256), CV_8UC3);
//下面是具体绘制直方图的代码
//因为图像中的某个灰度值的总个数可能会超出所定义的图像的尺寸,所以要对个数进行归一化处理
//先用 minMaxLoc函数来得到计算直方图中的最大数据,这个函数的使用方法大家一看函数原型便知
double g_dHistMaxValue;
minMaxLoc(dstHist, 0, &g_dHistMaxValue, 0, 0);
//遍历直方图数据,对数据进行归一化和绘图处理
for (int i = 0; i < 256; i++)
int value = cvRound(dstHist.at<float>(i) * 256 * 0.9 / g_dHistMaxValue);
line(drawImage, Point(i, drawImage.rows - 1), Point(i, drawImage.rows - 1 - value), Scalar(255, 0, 0));
imshow("【直方图】", drawImage);
waitKey(0);
return 0;
运行结果如下图所示:
从绘制的直方图可以看出,原图的灰度值是集中于偏低区域的,即图像是比较暗的。
延伸阅读:
详解什么叫二维直方图,并利用OpenCV的函数calcHist()绘制图像的H-S二维直方图
以上是关于对OpenCV的函数calcHist()进行透彻解析,并利用它实例绘制一个一维灰度直方图的主要内容,如果未能解决你的问题,请参考以下文章
OpenCV RGB直方图计算与绘制----calcHist()函数normalize()函数
利用OpenCV的函数calcHist()计算出图像的直方图数据后绘制图像的直方图
利用OpenCV的函数calcHist()计算出图像的直方图数据后绘制图像的直方图