Opencv中直方图函数calcHist
calcHist函数在Opencv中是极难理解的一个函数,一方面是参数说明晦涩难懂,另一方面,说明书给出的实例也不足以令人完全搞清楚该函数的使用方式。最难理解的是第6,7,8个参数dims、histSize和ranges。以前一直都是想当然认为,该函数可以一次统计多张图片每个通道的灰度值数据,实际上calcHist函数一次只能统计一个通道上的直方图。我估计许多同学都犯过和我类似的错误,认为第5个参数hist,可以根据dims设定维度,比如dims=3,则输出的hist的维度就是3,并且会想当然的认为这个三维矩阵会保存三个通道上的直方图统计,悲哀的是,错了。它实际上是在三维坐标上统计的直方图,比如以第0个通道的灰度值有效统计范围(比如0~255)作为纵坐标,类似于笛卡尔坐标的y轴;以第1个通道灰度值统计有效范围作为横坐标,类似于笛卡尔坐标的x轴;以第2个通道的灰度值有效统计范围作为z轴。这三个轴坐标作为统计的依据。比如坐标(6,7,8),表示统计满足“0通道上灰度值为1;1通道上灰度为7;2通道上灰度为8的像素”,在整个图像上的像素个数。
当然,当dims=1时,就好理解了,就是对某一个单一通道上的灰度值直方图统计。
还有很重要一点,那就是,calcHist函数有bug。当图像尺度小于9*12,直方图均匀分布,并且直方条个数histSize为4时,会有统计错误,在后面的实例中我们会看到。
calcHist函数的声明如下:
OpenCV提供了两个重载的calcHist函数,它可以计算一系列阵列的直方图,这些系列通常是图像或像平面。它最多可以同时处理32个图像。
C++: 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 )
C++: void calcHist(const Mat* images, int nimages, const int* channels, InputArray mask, SparseMat& hist, int dims, const int* histSize, const float** ranges, bool uniform = true, bool accumulate= false )
参数说明:
images – 源图像数组,它们有同样的位深CV_8U或 CV_32F ,同样的尺寸;图像阵列中的每一个图像都可以有任意多个通道;
nimages – 源图像的数目。
channels – 维度通道序列,第一幅图像的通道标号从0~image[0].channels( )-1。Image[0]表示图像数组中的第一幅图像,channels()表示该图像的通道数量。同理,图像阵列中的第二幅图像,通道标号从image[0].channerls( )开始,到image[1].channels( )-1为止;第三、四幅图像的通道标号顺序依此类推;也就是说图像阵列中的所有图像的通道根据图像排列顺序,排成一个通道队列。
mask – 可选择的mask。如果该矩阵不空的话,它必须是一个8-bit的矩阵,与images[i]同尺寸。在图像中,只有被mask覆盖的区域的像素才参与直方图统计。如果这个参数想用默认值,输入Mat()就可以了。
hist – 输出直方图, 它是一个稠密或稀疏矩阵,具有dims个维度;
dims – 直方图的维度,一定是正值, CV_MAX_DIMS(当前OpenCV版本是32个);
histSize – 数组,即histSize[i]表示第i个维度上bin的个数;这里的维度可以理解为通道。
ranges – 当uniform=true时,ranges是多个二元数组组成的数组;当uniform=false时,ranges是多元数组组成的数组。当在每个维度(或通道)上每个直方条等宽时,即uniform=true时,灰度值的有效统计范围的下界用L0表示,上界用UhistSize[i]-1表示,角标中的i表示第i个维度(或通道),上下界值可以表示为hrange[i]={ L0, UhistSize[i]-1}, 在统计时, L0和UhistSize[i]-1不在统计范围内。而ranges={ hrange[0], hrange[1], …… , hrange[dims]}。ranges的元素个数由参数dims决定。
其中,L0表示在该通道上第0个直方条(bin)的下边界,UhistSize[i]-1表示最后一个直方条histSize[i]-1的上边界。在该维度上直方条的个数为histSize[i],如hrange[0]={ L0,
UhistSize[0]},hrange[1]={ L1, UhistSize[1]}, hrange[2]={
L2, UhistSize[2]}, …… , hrange[dims]={ L0, UhistSize[0]}。
当uniform=false时,ranges中的每个元素ranges[i]都是一个多元数组,元素个数为histSize[i]+1,它们分别是:L0 , U0=L1, U1= L2,
…… ,UhistSize[i]-2 , LhistSize[i]-1 , UhistSize[i]-1
。所以,ranges[i]={ L0 , L1, L2, …… , LhistSize[i]-1 ,UhistSize[i]-1}
uniform – 标识,用于说明直方条bin是否是均匀等宽的。
accumulate – 累积标识。如果该项设置,当直方图重新分配时,直方图在开始清零。这个特征可以使你通过几幅图像,累积计算一个简单的直方图,或者及时更新直方图。
函数calcHist可以计算一幅或多幅图像的直方图。在元组中增量一个直方图的时候,就是从输入图像组中的原位置提取一幅图像,并计算出它的直方图,并添加到元组中。
当参数dims>1时,输出矩阵Hist是二维矩阵。
calcHist示例:单通道灰度图像
int main()
{
Mat hist;
//这是一个9行12列的单通道图像
Mat img=(Mat_<uchar>(,)
<<,,,,,,,,,,,,
,,,,,,,,,,,,
,,,,,,,,,,,,
,,,,,,,,,,,,
,,,,,,,,,,,,
,,,,,,,,,,,,
,,,,,,,,,,,,
,,,,,,,,,,,,
,,,,,,,,,,,);
//灰度值有效范围是1~12,所以设定为hr1[]={1,13};将灰度范围均分为3部分,三个直方条取值范围分别为:第1部分1~4,第2部分5~8,第3部分9~12。
//因此令histSize[1]={3},这里的3意味着整个图像灰度范围1~12被均分为3部分,分别统计像素个数。
int histSize[]={};
int channels[]={};
float hr1[]={,};//矩阵b的灰度值有效范围是1<=V<13
const float* ranges[]={hr1};
calcHist(&img,,channels,Mat(),hist,,histSize,ranges,true);
cout<<"img="<<endl<<img<<endl;
cout<<"hist="<<endl<< hist<<endl;
return ;
}
输出结果如下:

本来一切都很完美,不过可惜,该函数有bug。
calcHist函数的bug
依然以上面的示例为例,当histSize[1]={3};或{6};或{12}都不会有问题。可是当histSize[1]={2};或{4}时,虽然uniform=true,但是在整个图像上灰度范围上,并没有被平均分割为2个或4个灰度直方条进行像素统计。输出结果如下所示:

经过测试,猜测,大概直方条的宽度含有3的整数倍时,均匀直方统计就会失效。如将上例中的图像改成9*15,灰度取值范围1~15,直方条个数为5,即直方条的宽度为3,则该函数的均匀直方统计就会失效,如下左图。然而,当直方条宽度为5时,则有正确输出结果,如下右图:

不过也有解决的办法,就是将灰度值统计范围的左值,也就是L0,减1,即L0=L0-1。还是以上面的例子为例,灰度值有效范围:1~15。令bin=3,但是hr1={0,16},经验证可以得到正确的统计结果,如下图所示:

其他数据大家可以自行验证,都是正确的。
Opencv中直方图函数calcHist的更多相关文章
- 【Opencv】直方图函数 calchist()
calchist函数需要包含头文件 #include <opencv2/imgproc/imgproc.hpp> 函数声明(三个重载 calchist函数): //! computes t ...
- 【计算机视觉】OpenCV中直方图处理函数简述
计算直方图calcHist 直方图是对数据集合的统计 ,并将统计结果分布于一系列提前定义的bins中.这里的数据不只指的是灰度值 ,统计数据可能是不论什么能有效描写叙述图像的特征. 如果有一个矩阵包括 ...
- OpenCV中phase函数计算方向场
一.函数原型 该函数参数angleInDegrees默认为false,即弧度,当置为true时,则输出为角度. phase函数根据函数来计算角度,计算精度大约为0.3弧度,当x,y相等时,angle ...
- OpenCV中threshold函数的使用
转自:https://blog.csdn.net/u012566751/article/details/77046445 一篇很好的介绍threshold文章: 图像的二值化就是将图像上的像素点的灰度 ...
- 5. openCV中常用函数学习
一.前言 经过两个星期的努力,一边学习,一边写代码,初步完成了毕业论文系统的界面和一些基本功能,主要包括:1 数据的读写和显示,及相关的基本操作(放大.缩小和移动):2 样本数据的选择:3 数据归一化 ...
- openCV中直方图均衡化算法的理解
直方图均衡化就是调整灰度直方图的分布,即将原图中的灰度值映射为一个新的值.映射的结果直观表现是灰度图的分布变得均匀,从0到255都有分布,不像原图那样集中.图像上的表现就是对比度变大,亮的更亮,暗的更 ...
- 【短道速滑一】OpenCV中cvResize函数使用双线性插值缩小图像到长宽大小一半时速度飞快(比最近邻还快)之异象解析和自我实现。
今天,一个朋友想使用我的SSE优化Demo里的双线性插值算法,他已经在项目里使用了OpenCV,因此,我就建议他直接使用OpenCV,朋友的程序非常注意效率和实时性(因为是处理视频),因此希望我能测试 ...
- OpenCV中cvWaitKey()函数注意事项
注意:这个函数是HighGUI中唯一能够获取和操作事件的函数,所以在一般的事件处理中,它需要周期地被调用,除非HighGUI被用在某些能够处理事件的环境中.比如在MFC环境下,这个函数不起作用.
- OpenCV中 常用 函数 的作用
1.CV_Assert函数作用: CV_Assert()若括号中的表达式值为false,则返回一个错误信息.
随机推荐
- pthread_cond_wait
while(1) 33 { 34 mm* p = NULL; 35 pthread_mutex_lock(&mutex); 36 while(head == NULL) 37 pthread_ ...
- 给DataFrame的列命名或重命名
1.读取文件的时候重命名 names = new_col,可以在读取文件的时候,给出新列名. new_col = ['new1', 'new2',... , 'newn'] pd.read_csv(' ...
- nginx在一个服务器上配置两个项目,并通过两个不同的域名访问
Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,由俄罗斯的程序设计师Igor Sysoev所开发,其特点是占有内存少,并发能力强. 话不多说,先从最基 ...
- css 设置头像图片不变形
css 设置头像图片不变形 在样式中加 object-fit: cover 就可以了
- 随机验证码生成和join 字符串
函数:string.join() Python中有join()和os.path.join()两个函数,具体作用如下: join(): 连接字符串数组.将字符串.元组.列表中的元素以指定的字符(分隔符) ...
- c++11:lambda表达式的使用
lambda表达式的一般形式: [capture list] (parameter list) -> return type{function body}; 其中,capture list (捕 ...
- #5 DIV2 A POJ 3321 Apple Tree 摘苹果 构建线段树
Apple Tree Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 25232 Accepted: 7503 Descr ...
- POJ 1149 猪圈买猪 建图太强大!! 没有透彻领悟 慢慢消化
PIGS Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 19575 Accepted: 8948 Description ...
- sh_10_字典基本使用
sh_10_字典基本使用 xiaoming_dict = {"name": "小明"} # 1. 取值 print(xiaoming_dict["na ...
- [DTOJ3996]:Lesson5!(DP+拓扑+线段树)
题目描述 “最短的捷径就是绕远路,绕远路就是我最短的捷径” 转眼就$Stage\ X$了,$Stage\ X$的比赛路线可以看做一个$n$个点$m$条边的有向无环图,每条边长度都是$1$.杰洛$\cd ...