zoukankan      html  css  js  c++  java
  • 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>(9,12)
               <<1,2,3,4,5,6,7,8,9,10,11,12,
                    1,2,3,4,5,6,7,8,9,10,11,12,
                    1,2,3,4,5,6,7,8,9,10,11,12,
                    1,2,3,4,5,6,7,8,9,10,11,12,
                    1,2,3,4,5,6,7,8,9,10,11,12,
                    1,2,3,4,5,6,7,8,9,10,11,12,
                    1,2,3,4,5,6,7,8,9,10,11,12,
                    1,2,3,4,5,6,7,8,9,10,11,12,
                    1,2,3,4,5,6,7,8,9,10,11,12);
        
    //灰度值有效范围是1~12,所以设定为
    hr1[]={1,13};将灰度范围均分为3部分,三个直方条取值范围分别为:第1部分1~4,第2部分5~8,第3部分9~12。
        //因此令histSize[1]={3},这里的3意味着整个图像灰度范围1~12被均分为3部分,分别统计像素个数。
        int histSize[1]={3};
        int channels[1]={0};
        float hr1[]={1,13};//矩阵b的灰度值有效范围是1<=V<13
        const float* ranges[]={hr1};
    
        calcHist(&img,1,channels,Mat(),hist,1,histSize,ranges,true);
        cout<<"img="<<endl<<img<<endl;
        cout<<"hist="<<endl<< hist<<endl;
        return 0;
    }

    输出结果如下:

    本来一切都很完美,不过可惜,该函数有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},经验证可以得到正确的统计结果,如下图所示:

    其他数据大家可以自行验证,都是正确的。

  • 相关阅读:
    寒假的Java学习笔记总结1
    寒假的Java学习笔记总结0
    归档七
    数组归档
    课后作业感悟2
    作业感悟1
    《大道至简》第二章(是懒人创造了方法)读后感
    postman接口测试-文件上传
    postman接口测试:获取所有信息
    怎么测接口?
  • 原文地址:https://www.cnblogs.com/phoenixdsg/p/8280362.html
Copyright © 2011-2022 走看看