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的更多相关文章

  1. 【Opencv】直方图函数 calchist()

    calchist函数需要包含头文件 #include <opencv2/imgproc/imgproc.hpp> 函数声明(三个重载 calchist函数): //! computes t ...

  2. 【计算机视觉】OpenCV中直方图处理函数简述

    计算直方图calcHist 直方图是对数据集合的统计 ,并将统计结果分布于一系列提前定义的bins中.这里的数据不只指的是灰度值 ,统计数据可能是不论什么能有效描写叙述图像的特征. 如果有一个矩阵包括 ...

  3. OpenCV中phase函数计算方向场

    一.函数原型 ​该函数参数angleInDegrees默认为false,即弧度,当置为true时,则输出为角度. phase函数根据函数来计算角度,计算精度大约为0.3弧度,当x,y相等时,angle ...

  4. OpenCV中threshold函数的使用

    转自:https://blog.csdn.net/u012566751/article/details/77046445 一篇很好的介绍threshold文章: 图像的二值化就是将图像上的像素点的灰度 ...

  5. 5. openCV中常用函数学习

    一.前言 经过两个星期的努力,一边学习,一边写代码,初步完成了毕业论文系统的界面和一些基本功能,主要包括:1 数据的读写和显示,及相关的基本操作(放大.缩小和移动):2 样本数据的选择:3 数据归一化 ...

  6. openCV中直方图均衡化算法的理解

    直方图均衡化就是调整灰度直方图的分布,即将原图中的灰度值映射为一个新的值.映射的结果直观表现是灰度图的分布变得均匀,从0到255都有分布,不像原图那样集中.图像上的表现就是对比度变大,亮的更亮,暗的更 ...

  7. 【短道速滑一】OpenCV中cvResize函数使用双线性插值缩小图像到长宽大小一半时速度飞快(比最近邻还快)之异象解析和自我实现。

    今天,一个朋友想使用我的SSE优化Demo里的双线性插值算法,他已经在项目里使用了OpenCV,因此,我就建议他直接使用OpenCV,朋友的程序非常注意效率和实时性(因为是处理视频),因此希望我能测试 ...

  8. OpenCV中cvWaitKey()函数注意事项

    注意:这个函数是HighGUI中唯一能够获取和操作事件的函数,所以在一般的事件处理中,它需要周期地被调用,除非HighGUI被用在某些能够处理事件的环境中.比如在MFC环境下,这个函数不起作用.

  9. OpenCV中 常用 函数 的作用

    1.CV_Assert函数作用: CV_Assert()若括号中的表达式值为false,则返回一个错误信息.

随机推荐

  1. 使用GitHub(三):使用VSCode+GitHub进行版本控制

    使用GitHub(三):使用VSCode+GitHub进行版本控制 本文简单介绍使用VSCode+GitHub进行项目或者代码的版本控制.本文主要目的是对学习内容进行总结以及方便日后查阅. 详细教程和 ...

  2. c3p0连接池:com.mysql.cj.exceptions.InvalidConnectionAttributeException

    1 遇到的错误com.mysql.cj.exceptions.InvalidConnectionAttributeException: 四月 17, 2019 10:21:13 上午 com.mcha ...

  3. 2017 网易游戏互娱游戏研发4.21(offer)

    网易游戏互娱(offer) 去年这个时候就参加过网易游戏的实习生招聘,到今年总共收到了4次拒信.不过这次运气好,终于get了最想要的offer.去年实习生互娱笔试挂,秋招笔试挂,今年春招互娱投了连笔试 ...

  4. C#中无边框窗体移动或拖控件移动窗体

    [DllImport("user32.dll")] public static extern bool ReleaseCapture(); [DllImport("use ...

  5. zabbix监控win服务器

    https://jingyan.baidu.com/article/fcb5aff76486f2edaa4a712a.html 卸载win上的zabbix: cmd /c "C:\zabbi ...

  6. Spring、SpringMVC和Springboot的区别

    spring boot就是一个大框架里面包含了许许多多的东西,其中spring就是最核心的内容之一,当然就包含spring mvc. spring mvc 是只是spring 处理web层请求的一个模 ...

  7. linux中more命令如何使用

         more命令,功能类似 cat ,cat命令是整个文件的内容从上到下显示在屏幕上.兄弟连Linux培训教程() more会以一页一页的显示方便使用者逐页阅读,而最基本的指令就是按空白键(sp ...

  8. Word:转换PDF

    本文适用于Word 2007 + Windows 7,造冰箱的大熊猫@cnblogs 2018/8/3 一.Word文档转PDF文档 把Word文档转换为PDF,有两个免费解决方案 1.Microso ...

  9. AcWing:246. 区间最大公约数(线段树 + 增量数组(树状数组) + 差分序列)

    给定一个长度为N的数列A,以及M条指令,每条指令可能是以下两种之一: 1.“C l r d”,表示把 A[l],A[l+1],…,A[r] 都加上 d. 2.“Q l r”,表示询问 A[l],A[l ...

  10. JavaWeb_(MVC)管理员后台商品查询demo

    MVC分层实现管理员后台商品查询 MVC层即model view controller Model(模型):模型代表着核心的业务逻辑和数据(不要理解成Model只是实体类) View(视图):视图应该 ...