【第二部分 图像处理】第3章 Opencv图像处理进阶【3 直方图与匹配 A】

3.1图像直方图概述

直方图是对数据的集合统计 ,并将统计结果分布于一系列预定义的 bins 中。这里的数据 不仅仅指的是灰度值,统计数据可能是任何能有效描述图像的特征。先看一个例子吧。 假设有一个矩阵包含一张图像的信息 (灰度值0-255 ):

这里写图片描述

图1

如果我们按照某种方式去统计这些数字,会发生什么情况呢? 既然已知数字的范围包含 256 个值, 我们可以将这个范围分割成子区域(称作 bins), 如:
这里写图片描述
然后再统计掉入每一个 bini b i n i 的像素数目。采用这一方法来统计上面的数字矩阵,我们可以得到下图( x轴表示 bin, y轴表示各个bin中的像素个数)。
这里写图片描述

图2

以上只是一个说明直方图如何工作以及它的用处的简单示例。直方图可以统计的不仅仅是颜色灰度, 它可以统计任何图像特征 (如梯度, 方向等等)。让我们再来搞清楚直方图的一些具体细节:
dims: 需要统计的特征的数目, 在上例中, dims = 1 因为我们仅仅统计了灰度值(灰度图像)。
bins: 每个特征空间 子区段 的数目,在上例中, bins = 16
range: 每个特征空间的取值范围,在上例中, range = [0,255]
怎样去统计两个特征呢? 在这种情况下, 直方图就是3维的了,x轴和y轴分别代表一个特征, z轴是掉入 组合中的样本数目。 同样的方法适用于更高维的情形 (当然会变得很复杂)。

3.2直方图的计算与绘制

OpenCV提供了一个简单的计算数组集(通常是图像或分割后的通道)的直方图函数 calcHist 。 支持高达 32 维的直方图。

3.2.1直方图的计算和绘制相关API及源码

3.2.1.1 API

计算直方图:calcHist()函数

C++: 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 )
C++: void cv::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 )
C++: void cv::calcHist  (InputArrayOfArrays     images,
                         const std::vector< int > &     channels,
                         InputArray     mask,
                         OutputArray    hist,
                         const std::vector< int > &     histSize,
                         const std::vector< float > &   ranges,
                         bool   accumulate = false )    

【参数】
第一个参数,images Source arrays. They all should have the same depth, CV_8U, CV_16U or CV_32F , and the same size. Each of them can have an arbitrary number of channels.
第二个参数,nimages Number of source images.
第三个参数,channels List of the dims channels used to compute the histogram. The first array channels are numerated from 0 to images[0].channels()-1 , the second array channels are counted from images[0].channels() to images[0].channels() + images[1].channels()-1, and so on.
第四个参数,mask Optional mask. If the matrix is not empty, it must be an 8-bit array of the same size as images[i] . The non-zero mask elements mark the array elements counted in the histogram.
第五个参数,hist Output histogram, which is a dense or sparse dims -dimensional array.
第六个参数,dims Histogram dimensionality that must be positive and not greater than CV_MAX_DIMS (equal to 32 in the current OpenCV version).
第七个参数,histSize Array of histogram sizes in each dimension.
第八个参数,ranges Array of the dims arrays of the histogram bin boundaries in each dimension. When the histogram is uniform ( uniform =true), then for each dimension i it is enough to specify the lower (inclusive) boundary of the 0-th histogram bin and the upper (exclusive) boundary for the last histogram bin histSize[i]-1 . That is, in case of a uniform histogram each of ranges[i] is an array of 2 elements. When the histogram is not uniform ( uniform=false ), then each of ranges[i] contains histSize[i]+1 elements: . The array elements, that are not between and , are not counted in the histogram.
第九个参数,uniform Flag indicating whether the histogram is uniform or not (see above).
第十个参数,accumulate Accumulation flag. If it is set, the histogram is not cleared in the beginning when it is allocated. This feature enables you to compute a single histogram from several sets of arrays, or to update the histogram in time.

寻找最值:minMaxLoc

C++: void minMaxLoc(InputArray src, 
                    double* minVal, 
                    double* maxVal=0, 
                    Point* minLoc=0, 
                    Point* maxLoc=0, 
                    InputArray mask=noArray())
C++: void minMaxLoc(const SparseMat& src, 
                    double* minVal, 
                    double* maxVal, 
                    int* minIdx=0, 
                    int* maxIdx=0)

【参数】
第一个参数,src – Source single-channel array.
第二个参数,minVal – Pointer to the returned minimum value. NULL is used if not required.
第三个参数,maxVal – Pointer to the returned maximum value. NULL is used if not required.
第四个参数,minLoc – Pointer to the returned minimum location (in 2D case). NULL is used if not required.
第五个参数,maxLoc – Pointer to the returned maximum location (in 2D case). NULL is used if not required.
第六个参数,mask – Optional mask used to select a sub-array.
【注】minMaxLoc()函数存在两个版本,第一个版本最常用。

3.2.1.2源代码

calcHist()函数

/*【calcHist ( )源代码】***************************************************************
 * @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的对应版本源码完全一样,均在对应的安装目录下)  
 * @源码路径:…\opencv\sources\modules\imgproc\src\ histogram.cpp
 * @起始行数:1223行   
********************************************************************************/
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, bool accumulate )
{
    Mat mask = _mask.getMat();

    CV_Assert(dims > 0 && histSize);

    const uchar* const histdata = _hist.getMat().ptr();
    _hist.create(dims, histSize, CV_32F);
    Mat hist = _hist.getMat(), ihist = hist;
    ihist.flags = (ihist.flags & ~CV_MAT_TYPE_MASK)|CV_32S;

#ifdef HAVE_IPP
    CV_IPP_CHECK()
    {
        if (nimages == 1 && images[0].type() == CV_8UC1 && dims == 1 && channels &&
                channels[0] == 0 && mask.empty() && images[0].dims <= 2 &&
                !accumulate && uniform)
        {
            ihist.setTo(Scalar::all(0));
            AutoBuffer<Ipp32s> levels(histSize[0] + 1);

            bool ok = true;
            const Mat & src = images[0];
            int nstripes = std::min<int>(8, static_cast<int>(src.total() / (1 << 16)));
#ifdef HAVE_CONCURRENCY
            nstripes = 1;
#endif
            IPPCalcHistInvoker invoker(src, ihist, levels, histSize[0] + 1, (Ipp32s)ranges[0][0], (Ipp32s)ranges[0][1], &ok);
            Range range(0, src.rows);
            parallel_for_(range, invoker, nstripes);

            if (ok)
            {
                ihist.convertTo(hist, CV_32F);
                CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT);
                return;
            }
            setIppErrorStatus();
        }
    }
#endif

    if( !accumulate || histdata != hist.data )
        hist = Scalar(0.);
    else
        hist.convertTo(ihist, CV_32S);

    std::vector<uchar*> ptrs;
    std::vector<int> deltas;
    std::vector<double> uniranges;
    Size imsize;

    CV_Assert( mask.empty() || mask.type() == CV_8UC1 );
    histPrepareImages( images, nimages, channels, mask, dims, hist.size, ranges,
                       uniform, ptrs, deltas, imsize, uniranges );
    const double* _uniranges = uniform ? &uniranges[0] : 0;

    int depth = images[0].depth();

    if( depth == CV_8U )
        calcHist_8u(ptrs, deltas, imsize, ihist, dims, ranges, _uniranges, uniform );
    else if( depth == CV_16U )
        calcHist_<ushort>(ptrs, deltas, imsize, ihist, dims, ranges, _uniranges, uniform );
    else if( depth == CV_32F )
        calcHist_<float>(ptrs, deltas, imsize, ihist, dims, ranges, _uniranges, uniform );
    else
        CV_Error(CV_StsUnsupportedFormat, "");

    ihist.convertTo(hist, CV_32F);
}
/*【calcHist ( )源代码】***************************************************************
 * @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的对应版本源码完全一样,均在对应的安装目录下)  
 * @源码路径:…\opencv\sources\modules\imgproc\src\ histogram.cpp
 * @起始行数:1537行   
********************************************************************************/
void cv::calcHist( const Mat* images, int nimages, const int* channels,
               InputArray _mask, SparseMat& hist, int dims, const int* histSize,
               const float** ranges, bool uniform, bool accumulate )
{
    Mat mask = _mask.getMat();
    calcHist( images, nimages, channels, mask, hist, dims, histSize,
              ranges, uniform, accumulate, false );
}
/*【calcHist ( )源代码】***************************************************************
 * @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的对应版本源码完全一样,均在对应的安装目录下)  
 * @源码路径:…\opencv\sources\modules\imgproc\src\ histogram.cpp
 * @起始行数:1537行   
********************************************************************************/
void cv::calcHist( InputArrayOfArrays images, const std::vector<int>& channels,
                   InputArray mask, OutputArray hist,
                   const std::vector<int>& histSize,
                   const std::vector<float>& ranges,
                   bool accumulate )
{
    CV_OCL_RUN(images.total() == 1 && channels.size() == 1 && images.channels(0) == 1 &&
               channels[0] == 0 && images.isUMatVector() && mask.empty() && !accumulate &&
               histSize.size() == 1 && histSize[0] == BINS && ranges.size() == 2 &&
               ranges[0] == 0 && ranges[1] == BINS,
               ocl_calcHist(images, hist))

    int i, dims = (int)histSize.size(), rsz = (int)ranges.size(), csz = (int)channels.size();
    int nimages = (int)images.total();

    CV_Assert(nimages > 0 && dims > 0);
    CV_Assert(rsz == dims*2 || (rsz == 0 && images.depth(0) == CV_8U));
    CV_Assert(csz == 0 || csz == dims);
    float* _ranges[CV_MAX_DIM];
    if( rsz > 0 )
    {
        for( i = 0; i < rsz/2; i++ )
            _ranges[i] = (float*)&ranges[i*2];
    }

    AutoBuffer<Mat> buf(nimages);
    for( i = 0; i < nimages; i++ )
        buf[i] = images.getMat(i);

    calcHist(&buf[0], nimages, csz ? &channels[0] : 0,
            mask, hist, dims, &histSize[0], rsz ? (const float**)_ranges : 0,
            true, accumulate);
}

minMaxLoc()函数

/*【minMaxLoc ( )源代码】************************************************************
 * @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的对应版本源码完全一样,均在对应的安装目录下)  
 * @源码路径:…\opencv\sources\modules\core\src\stat.cpp
 * @起始行数:2335行   
********************************************************************************/
void cv::minMaxLoc( InputArray _img, double* minVal, double* maxVal,
                    Point* minLoc, Point* maxLoc, InputArray mask )
{
    CV_Assert(_img.dims() <= 2);

    minMaxIdx(_img, minVal, maxVal, (int*)minLoc, (int*)maxLoc, mask);
    if( minLoc )
        std::swap(minLoc->x, minLoc->y);
    if( maxLoc )
        std::swap(maxLoc->x, maxLoc->y);
}
/*【minMaxLoc ( )源代码】************************************************************
 * @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的对应版本源码完全一样,均在对应的安装目录下)  
 * @源码路径:…\opencv\sources\modules\core\src\matrix.cpp
 * @起始行数: 5264行   
********************************************************************************/
void minMaxLoc( const SparseMat& src, double* _minval, double* _maxval, int* _minidx, int* _maxidx )
{
    SparseMatConstIterator it = src.begin();
    size_t i, N = src.nzcount(), d = src.hdr ? src.hdr->dims : 0;
    int type = src.type();
    const int *minidx = 0, *maxidx = 0;

    if( type == CV_32F )
    {
        float minval = FLT_MAX, maxval = -FLT_MAX;
        for( i = 0; i < N; i++, ++it )
        {
            float v = it.value<float>();
            if( v < minval )
            {
                minval = v;
                minidx = it.node()->idx;
            }
            if( v > maxval )
            {
                maxval = v;
                maxidx = it.node()->idx;
            }
        }
        if( _minval )
            *_minval = minval;
        if( _maxval )
            *_maxval = maxval;
    }
    else if( type == CV_64F )
    {
        double minval = DBL_MAX, maxval = -DBL_MAX;
        for( i = 0; i < N; i++, ++it )
        {
            double v = it.value<double>();
            if( v < minval )
            {
                minval = v;
                minidx = it.node()->idx;
            }
            if( v > maxval )
            {
                maxval = v;
                maxidx = it.node()->idx;
            }
        }
        if( _minval )
            *_minval = minval;
        if( _maxval )
            *_maxval = maxval;
    }
    else
        CV_Error( CV_StsUnsupportedFormat, "Only 32f and 64f are supported" );

    if( _minidx )
        for( i = 0; i < d; i++ )
            _minidx[i] = minidx[i];
    if( _maxidx )
        for( i = 0; i < d; i++ )
            _maxidx[i] = maxidx[i];
}

3.2.2绘制H-S直方图实例

代码参看附件【demo1】。

这里写图片描述

图3

3.2.3计算并绘制一维直方图实例

代码参看附件【demo2】。

这里写图片描述

图4原图

这里写图片描述

图5直方图

3.2.4绘制RGB三色直方图

代码参看附件【demo3】。

这里写图片描述

图6原图

这里写图片描述

图7三色直方图

参考:
中文
英文

本章参考附件

点击进入

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Bruceoxl

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

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

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

打赏作者

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

抵扣说明:

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

余额充值