OpenCV中的直方图计算函数calcHist函数可以计算给定的若干幅图像的指定的通道的统计直方图!

calcHist函数原型为

     //!计算给定图像集合的联合密度直方图 (joint dense histogram)

CV_EXPORTS 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 );

该函数内部的实现方式是怎样的呢?

首先调用了函数 histPrepareImages(....)准备一些中间变量,

//该函数主要初始化了三个中间临时变量  ptrs,deltas,uniranges
static void histPrepareImages( const Mat* images, int nimages, const int* channels,
                               const Mat& mask, int dims, const int* histSize, const float** ranges, bool uniform,
                               vector<uchar*>& ptrs, vector<int>& deltas, Size& imsize, vector<double>& uniranges )

接着根据输入图像的位深度调用不同的函数实现

针对8位深度的图像,调用了函数calcHist_8u(.....)

static void
calcHist_8u( vector<uchar*>& _ptrs, const vector<int>& _deltas, Size imsize, Mat& hist, int dims, const float**                                _ranges, const double* _uniranges, bool uniform )

针对16位和32位深度图像,调用了模板类函数

template<typename T> static void
calcHist_( vector<uchar*>& _ptrs, const vector<int>& _deltas,  Size imsize, Mat& hist, int dims, const float**                                 _ranges, const double* _uniranges, bool uniform )

调用方式如下:

  int depth = images[0].depth();//输入图像的深度
//根据输入图像的位深度调用不同的直方图计算函数
    if( depth == CV_8U ) //8位深度
        calcHist_8u(ptrs, deltas, imsize, ihist, dims, ranges, _uniranges, uniform );
    else if( depth == CV_16U )//16位深度
        calcHist_<ushort>(ptrs, deltas, imsize, ihist, dims, ranges, _uniranges, uniform );
    else if( depth == CV_32F ) //32位深度
        calcHist_<float>(ptrs, deltas, imsize, ihist, dims, ranges, _uniranges, uniform );
    else
        CV_Error(CV_StsUnsupportedFormat, "");

为了研究源码,我把位于...\OpenCV245\modules\imgproc\src\文件夹下的histogram.cpp文件下的相关函数加到自定义命名空间中,这么做就可以调试运行每一行代码啦 

在命名空间MyHistFunc中定义了一系列与直方图的计算为主要目的之函数,所有在该Histogram.cpp文件中
被定义为 static 类型的函数,在头文件Histogram.h中都没有进行声明,所以这些静态函数都只能在该文件中
被该文件的定义在他之后的其他函数调用;而只有函数
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 )
在头文件中进行了声明,所以该函数是唯一暴露在外边,可以被其他文件中的函数(如Main()函数)调用
的函数。 如果你想在别的文件的函数中调用该文件中的static类型的函数,那么只需要把该函数的声明
复制到头文件Histogram.h中,就像calcHist(....)函数那样。

histogram.h 头文件的内容(这个头文件是在新建的工程中自己写的):

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/imgproc/imgproc_c.h"
#include "opencv2/core/internal.hpp"
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;
using namespace cv;

//自己定义了一个MyHistFunc的命名空间,然后把cv命名空间下的对应函数拷贝出来,
//以免编译器不知道该使用哪个命名空间中的calcHist(。。。)函数,在主函数调用时
//要加上命名空间的前缀,用法: MyHistFunc:: calcHist(....)

namespace MyHistFunc
{

	//! 计算给定的一系列图像的联合密度分布直方图(joint dense histogram)
	void 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 );
};

histogram.cpp 直方图函数实现文件的内容(这个头文件是在OpenCV的原文件上改动了命名空间的名字写成的):

#include "stdafx.h"
#include "Histogram.h"

/**
  在命名空间MyHistFunc中定义了一系列与直方图的计算为主要目的之函数,所有在该Histogram.cpp文件中
  被定义为 static 类型的函数,在头文件Histogram.h中都没有进行声明,所以这些静态函数都只能在该文件中
  被该文件的定义在他之后的其他函数调用;而只有函数
       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 )
   在头文件中进行了声明,所以该函数是唯一暴露在外边,可以被其他文件中的函数(如Main()函数)调用
   的函数。 如果你想在别的文件的函数中调用该文件中的static类型的函数,那么只需要把该函数的声明
   复制到头文件Histogram.h中,就像calcHist(....)函数那样。
*/

namespace MyHistFunc
{
	
//#define HAVE_TBB 1
	////////////////// Helper functions //////////////////////

static const size_t OUT_OF_RANGE = (size_t)1 << (sizeof(size_t)*8 - 2);

//不管有多少通道,对于8位深度的图像,单一通道上的每个像素点的取值范围是从[0,256),
//所以,在计算8位深度的图像的直方图的时候,总是假定每一个维度上的取值范围都是[0,256)
//然后根据参数中给出的每一维度的范围制作查找表;在每一维上,查找表的长度都是256,
//查找表中第i维第j的位置上存放的就是灰度值等于j的像素点所对应的i维度上的bin的编号
static void
calcHistLookupTables_8u( const Mat& hist, const SparseMat& shist,
                         int dims, const float** ranges, const double* uniranges,
                         bool uniform, bool issparse, vector& _tab )
{
    const int low = 0, high = 256;//位深度为8位的图像,一个基础元素的范围都在[0,255)内
    int i, j;
	//为查找表开辟空间,查找表长度 = 直方图维数*256
	//每一个维度所占的存储位置都是256个
    _tab.resize((high-low)*dims);
    size_t* tab = &_tab[0];//指向查找表数组头的指针

    if( uniform )  //均匀直方图,也就是同一维度内部的各个bin的区间宽度是一样的 
    {			   //但是不同的维度上的bin的区间宽度不一定是一样的
        for( i = 0; i < dims; i++ )
        {
            double a = uniranges[i*2];//第i维上单位区间内bin的上限
            double b = uniranges[i*2+1];
            int sz = !issparse ? hist.size[i] : shist.size(i);//第i维上可以容纳的bin数目
			//step[i]是第i维的步长(跨度),多维矩阵每一维度的步长不一样的
			//对于单通道32位深度的浮点数矩阵,第0维的指针移动步长是sizeof(float)即==4字节长度
			//第1维的指针移动步长是 cols*sizeof(float) = 4*hist.cols
            size_t step = !issparse ? hist.step[i] : 1;

			//下面这一循环就是把第i维上的每一个灰度值(0,255)压缩到参数规定的
			//区间{ranges[i][low]到ranges[i][high]}内,然后在对应位置上保存bin索引值
            for( j = low; j < high; j++ )// 灰度值j 从0到255 循环
            {
				//灰度值j在输入参数规定的第i维灰度区间内的索引值
                int idx = cvFloor(j*a + b);//该索引值是数学上线性运算后得到的

                size_t written_idx; //该索引是写入直方图矩阵时的索引值,
				
				//由于查找表是单行矩阵,而直方图是多维矩阵,所以索引idx要乘以直方图
				//每一维对应的跨度(step)才能与查找表的具体位置对应
                if( (unsigned)idx < (unsigned)sz )
                    written_idx = idx*step;//索引idx要乘以直方图每一维对应的跨度(step)
                else
                    written_idx = OUT_OF_RANGE;//对直方图的索引项不能超出直方图的总长度

				//对查找表第i维第j个位置的索引是256*i+j,该位置保存的是当灰度值为j时,应该访问的
				//直方图的对应位置的索引
                tab[i*(high - low) + j - low] = written_idx; //相当于tab[i*256+j] = written_idx;
            }
        }
    }
    else //非均匀直方图的情形,同一维度内各个bin的区间宽度不一样,从最小值到最大值非均匀划分
    {
        for( i = 0; i < dims; i++ )
        {
            int limit = std::min(cvCeil(ranges[i][0]), high);
            int idx = -1, sz = !issparse ? hist.size[i] : shist.size(i);
            size_t written_idx = OUT_OF_RANGE;
            size_t step = !issparse ? hist.step[i] : 1;

            for(j = low;;)
            {
				//低于第i维的最小区间的下界的那些灰度值
                for( ; j < limit; j++ )
                    tab[i*(high - low) + j - low] = written_idx;

				//在第i维指定区间内的灰度-索引值查找表
                if( (unsigned)(++idx) < (unsigned)sz )
                {
                    limit = std::min(cvCeil(ranges[i][idx+1]), high);
                    written_idx = idx*step;
                }
                else
                {
					//超出了第i维度规定的区间中的最大上界的那些灰度值 j
                    for( ; j < high; j++ )
                        tab[i*(high - low) + j - low] = OUT_OF_RANGE;
                    break;
                }
            }
        }
    }
}

//该函数主要初始化了三个中间临时变量  ptrs,deltas,uniranges
static void histPrepareImages( const Mat* images, int nimages, const int* channels,
                               const Mat& mask, int dims, const int* histSize,
                               const float** ranges, bool uniform,
                               vector& ptrs, vector& deltas,
                               Size& imsize, vector& uniranges )
{
    int i, j, c;
    CV_Assert( channels != 0 || nimages == dims );

	 
    imsize = images[0].size();//images是个图像数组,所有图像的尺寸必须一样的
	//输入图像的位深度,elemSize1 表示的是矩阵中数据类型的大小,即 elemSize/channels的大小
    int depth = images[0].depth(), esz1 = (int)images[0].elemSize1();//对于CV_8UCx类的图像,==1
    bool isContinuous = true; //数据在内存中是否连续存放

	//dims是要构建的直方图的维数,+1是因为ptrs中最后一个位置要留给掩膜矩阵mask呀
    ptrs.resize(dims + 1);//指向对应的图像数组首地址,数组中最后一个指针指向mask
    deltas.resize((dims + 1)*2);// +1 是因为deltas中最后两个位置要留给mask呀

    for( i = 0; i < dims; i++ )
    {
        if(!channels) 
        {//channels为空的时候,该句将执行
            
            c = 0;
            CV_Assert( images[i].channels() == 1 );

			CV_Assert( images[i].size() == imsize && images[i].depth() == depth );
            if( !images[i].isContinuous() )
               isContinuous = false;
			ptrs[i] = images[i].data + c*esz1; //ptrs指针指向对应的图像数据数组首地址,这时c==0
			deltas[i*2] = images[i].channels();//第j个图像的通道数目,指针ptrs[i]在图像中同一行内移动的步长
			deltas[i*2+1] = (int)(images[i].step/esz1 - imsize.width*deltas[i*2]);
			 
        }
        else
        {
            c = channels[i];
            CV_Assert( c >= 0 );
            for( j = 0; j < nimages; c -= images[j].channels(), j++ )
                if( c < images[j].channels() )
                    break;
            CV_Assert( j < nimages );

			CV_Assert( images[j].size() == imsize && images[j].depth() == depth );
            if( !images[j].isContinuous() )
               isContinuous = false;
			ptrs[i] = images[j].data + c*esz1; //ptrs指针指向对应的图像矩阵c通道的首地址
			deltas[i*2] = images[j].channels();//第j个图像的通道数目
			deltas[i*2+1] = (int)(images[j].step/esz1 - imsize.width*deltas[i*2]);
        }
        
    }

	//如果mask矩阵不为空,则初始化指向mask矩阵的数据的指针和步长
    if( mask.data )
    {
        CV_Assert( mask.size() == imsize && mask.channels() == 1 );
        isContinuous = isContinuous && mask.isContinuous();
        ptrs[dims] = mask.data;
        deltas[dims*2] = 1;  //列与列之间的步长,掩膜矩阵mask是单通道的,所以 ==1
		//mask.step/mask.elemSize1()是mask矩阵一行的字节数,
        deltas[dims*2 + 1] = (int)(mask.step/mask.elemSize1());//指针在行与行之间的步长
    }

#ifndef HAVE_TBB
    if( isContinuous )
    {
        imsize.width *= imsize.height;
        imsize.height = 1;
    }
#endif
	//ranges[][]是个具有dims行2列的数组,其中第i行ranges[i]代表了直方图第i维的取值区间
	//该区间包括下界ranges[i][0]和上界ranges[i][1]

    if( !ranges )  //ranges数组为空的话,说明参数中没有指定每一维的取值区间,
    {				//则默认所有维度的取值区间是(0,255),而且默认把区间[0,255]
					//划分成histSize个宽度一样的 bin
        CV_Assert( depth == CV_8U );

        uniranges.resize( dims*2 );
        for( i = 0; i < dims; i++ )
        {/*第i维上单位区间内直方图bin的数目*/
            uniranges[i*2] = histSize[i]/256.;
            uniranges[i*2+1] = 0;
        }
    }
    else if( uniform )//如果uniform为真,则说明直方图的每一维上的区间是均匀划分的
    {				  //也就是直方图每一维的所有bin的宽度是一样的
        uniranges.resize( dims*2 );
        for( i = 0; i < dims; i++ )
        {
			//保证第i维的取值范围区间的合理性(a,b)满足a static void
calcHist_( vector& _ptrs, const vector& _deltas,
           Size imsize, Mat& hist, int dims, const float** _ranges,
           const double* _uniranges, bool uniform )
{
    T** ptrs = (T**)&_ptrs[0];
    const int* deltas = &_deltas[0];
    uchar* H = hist.data;
    int i, x;
    const uchar* mask = _ptrs[dims];
    int mstep = _deltas[dims*2 + 1];
    int size[CV_MAX_DIM];
    size_t hstep[CV_MAX_DIM];

    for( i = 0; i < dims; i++ )
    {
        size[i] = hist.size[i];
        hstep[i] = hist.step[i];
    }

    if( uniform )
    {
        const double* uniranges = &_uniranges[0];

        if( dims == 1 )
        {
#ifdef HAVE_TBB
            calcHist1D_Invoker body(_ptrs, _deltas, hist, _uniranges, size[0], dims, imsize);
            parallel_for(BlockedRange(0, imsize.height), body);
            return;
#endif
            double a = uniranges[0], b = uniranges[1];
            int sz = size[0], d0 = deltas[0], step0 = deltas[1];
            const T* p0 = (const T*)ptrs[0];

            for( ; imsize.height--; p0 += step0, mask += mstep )
            {
                if( !mask )
                    for( x = 0; x < imsize.width; x++, p0 += d0 )
                    {
                        int idx = cvFloor(*p0*a + b);
                        if( (unsigned)idx < (unsigned)sz )
                            ((int*)H)[idx]++;
                    }
                else
                    for( x = 0; x < imsize.width; x++, p0 += d0 )
                        if( mask[x] )
                        {
                            int idx = cvFloor(*p0*a + b);
                            if( (unsigned)idx < (unsigned)sz )
                                ((int*)H)[idx]++;
                        }
            }
        }
        else if( dims == 2 )
        {
#ifdef HAVE_TBB
            calcHist2D_Invoker body(_ptrs, _deltas, hist, _uniranges, size, dims, imsize, hstep);
            parallel_for(BlockedRange(0, imsize.height), body);
            return;
#endif
            double a0 = uniranges[0], b0 = uniranges[1], a1 = uniranges[2], b1 = uniranges[3];
            int sz0 = size[0], sz1 = size[1];
            int d0 = deltas[0], step0 = deltas[1],
                d1 = deltas[2], step1 = deltas[3];
            size_t hstep0 = hstep[0];
            const T* p0 = (const T*)ptrs[0];
            const T* p1 = (const T*)ptrs[1];

            for( ; imsize.height--; p0 += step0, p1 += step1, mask += mstep )
            {
                if( !mask )
                    for( x = 0; x < imsize.width; x++, p0 += d0, p1 += d1 )
                    {
                        int idx0 = cvFloor(*p0*a0 + b0);
                        int idx1 = cvFloor(*p1*a1 + b1);
                        if( (unsigned)idx0 < (unsigned)sz0 && (unsigned)idx1 < (unsigned)sz1 )
                            ((int*)(H + hstep0*idx0))[idx1]++;
                    }
                else
                    for( x = 0; x < imsize.width; x++, p0 += d0, p1 += d1 )
                        if( mask[x] )
                        {
                            int idx0 = cvFloor(*p0*a0 + b0);
                            int idx1 = cvFloor(*p1*a1 + b1);
                            if( (unsigned)idx0 < (unsigned)sz0 && (unsigned)idx1 < (unsigned)sz1 )
                                ((int*)(H + hstep0*idx0))[idx1]++;
                        }
            }
        }
        else if( dims == 3 )
        {
#ifdef HAVE_TBB
            if( calcHist3D_Invoker::isFit(hist, imsize) )
            {
                calcHist3D_Invoker body(_ptrs, _deltas, imsize, hist, uniranges, dims, hstep, size);
                parallel_for(BlockedRange(0, imsize.height), body);
                return;
            }
#endif
            double a0 = uniranges[0], b0 = uniranges[1],
                   a1 = uniranges[2], b1 = uniranges[3],
                   a2 = uniranges[4], b2 = uniranges[5];
            int sz0 = size[0], sz1 = size[1], sz2 = size[2];
            int d0 = deltas[0], step0 = deltas[1],
                d1 = deltas[2], step1 = deltas[3],
                d2 = deltas[4], step2 = deltas[5];
            size_t hstep0 = hstep[0], hstep1 = hstep[1];
            const T* p0 = (const T*)ptrs[0];
            const T* p1 = (const T*)ptrs[1];
            const T* p2 = (const T*)ptrs[2];

            for( ; imsize.height--; p0 += step0, p1 += step1, p2 += step2, mask += mstep )
            {
                if( !mask )
                    for( x = 0; x < imsize.width; x++, p0 += d0, p1 += d1, p2 += d2 )
                    {
                        int idx0 = cvFloor(*p0*a0 + b0);
                        int idx1 = cvFloor(*p1*a1 + b1);
                        int idx2 = cvFloor(*p2*a2 + b2);
                        if( (unsigned)idx0 < (unsigned)sz0 &&
                            (unsigned)idx1 < (unsigned)sz1 &&
                            (unsigned)idx2 < (unsigned)sz2 )
                            ((int*)(H + hstep0*idx0 + hstep1*idx1))[idx2]++;
                    }
                else
                    for( x = 0; x < imsize.width; x++, p0 += d0, p1 += d1, p2 += d2 )
                        if( mask[x] )
                        {
                            int idx0 = cvFloor(*p0*a0 + b0);
                            int idx1 = cvFloor(*p1*a1 + b1);
                            int idx2 = cvFloor(*p2*a2 + b2);
                            if( (unsigned)idx0 < (unsigned)sz0 &&
                               (unsigned)idx1 < (unsigned)sz1 &&
                               (unsigned)idx2 < (unsigned)sz2 )
                                ((int*)(H + hstep0*idx0 + hstep1*idx1))[idx2]++;
                        }
            }
        }
        else
        {
            for( ; imsize.height--; mask += mstep )
            {
                if( !mask )
                    for( x = 0; x < imsize.width; x++ )
                    {
                        uchar* Hptr = H;
                        for( i = 0; i < dims; i++ )
                        {
                            int idx = cvFloor(*ptrs[i]*uniranges[i*2] + uniranges[i*2+1]);
                            if( (unsigned)idx >= (unsigned)size[i] )
                                break;
                            ptrs[i] += deltas[i*2];
                            Hptr += idx*hstep[i];
                        }

                        if( i == dims )
                            ++*((int*)Hptr);
                        else
                            for( ; i < dims; i++ )
                                ptrs[i] += deltas[i*2];
                    }
                else
                    for( x = 0; x < imsize.width; x++ )
                    {
                        uchar* Hptr = H;
                        i = 0;
                        if( mask[x] )
                            for( ; i < dims; i++ )
                            {
                                int idx = cvFloor(*ptrs[i]*uniranges[i*2] + uniranges[i*2+1]);
                                if( (unsigned)idx >= (unsigned)size[i] )
                                    break;
                                ptrs[i] += deltas[i*2];
                                Hptr += idx*hstep[i];
                            }

                        if( i == dims )
                            ++*((int*)Hptr);
                        else
                            for( ; i < dims; i++ )
                                ptrs[i] += deltas[i*2];
                    }
                for( i = 0; i < dims; i++ )
                    ptrs[i] += deltas[i*2 + 1];
            }
        }
    }
    else
    {
        // non-uniform histogram
        const float* ranges[CV_MAX_DIM];
        for( i = 0; i < dims; i++ )
            ranges[i] = &_ranges[i][0];

        for( ; imsize.height--; mask += mstep )
        {
            for( x = 0; x < imsize.width; x++ )
            {
                uchar* Hptr = H;
                i = 0;

                if( !mask || mask[x] )
                    for( ; i < dims; i++ )
                    {
                        float v = (float)*ptrs[i];
                        const float* R = ranges[i];
                        int idx = -1, sz = size[i];

                        while( v >= R[idx+1] && ++idx < sz )
                            ; // nop

                        if( (unsigned)idx >= (unsigned)sz )
                            break;

                        ptrs[i] += deltas[i*2];
                        Hptr += idx*hstep[i];
                    }

                if( i == dims )
                    ++*((int*)Hptr);
                else
                    for( ; i < dims; i++ )
                        ptrs[i] += deltas[i*2];
            }

            for( i = 0; i < dims; i++ )
                ptrs[i] += deltas[i*2 + 1];
        }
    }
}


static void
calcHist_8u( vector& _ptrs, const vector& _deltas,
             Size imsize, Mat& hist, int dims, const float** _ranges,
             const double* _uniranges, bool uniform )
{
    uchar** ptrs = &_ptrs[0];
    const int* deltas = &_deltas[0];
    uchar* H = hist.data;  //指向直方图数据的指针
    int x;
    const uchar* mask = _ptrs[dims];//指向掩膜矩阵的指针
    int mstep = _deltas[dims*2 + 1];//mask step ,掩膜矩阵行与行之间的步长
    vector _tab; //计算直方图时的查找表
	//计算直方图查找表
    calcHistLookupTables_8u( hist, SparseMat(), dims, _ranges, _uniranges, uniform, false, _tab );
    const size_t* tab = &_tab[0]; //指向直方图查找表数组头的指针

    if( dims == 1 )//一维直方图的情形
    {
#ifdef HAVE_TBB
        if( CalcHist1D_8uInvoker::isFit(hist, imsize) )
        {
            int treadsNumber = tbb::task_scheduler_init::default_num_threads();
            int grainSize = imsize.height/treadsNumber;
            tbb::mutex histogramWriteLock;

            CalcHist1D_8uInvoker body(_ptrs, _deltas, imsize, hist, dims, _tab, &histogramWriteLock);
            parallel_for(BlockedRange(0, imsize.height, grainSize), body);
            return;
        }
#endif
		//deltas[]应该是指针每次移动的步子
		//deltas[0],d0 是指针横向移动的步长,在一行内每次移动的步长等于通道数
		//deltas[1],step0 是纵向移动的步长,在行与行之间移动
        int d0 = deltas[0] ;   //其实就是输入图像的通道数
		int step0 = deltas[1];
        int matH[256] = { 0, };//该数组用于统计每一个亮度值出现的次数
        const uchar* p0 = (const uchar*)ptrs[0]; //指向图像数据的指针
		
		//外循环按行从上往下扫描,同时分别移动指向图像数据和掩膜矩阵的指针
        for( ; imsize.height--; p0 += step0, mask += mstep )
        {
            if( !mask ) //掩膜矩阵的行数和列数与输入图像矩阵的行和列一样
            {			//如果指向对应行的掩膜矩阵指针为空,则执行下面的语句
                if( d0 == 1 )//对应于单通道图像
                {
                    for( x = 0; x <= imsize.width - 4; x += 4 )
                    {
                        int t0 = p0[x], t1 = p0[x+1];
                        matH[t0]++; matH[t1]++;
                        t0 = p0[x+2]; t1 = p0[x+3];
                        matH[t0]++; matH[t1]++;
                    }
                    p0 += x;					
                }
                else     //对应于多通道图像
                {
					for( x = 0; x <= imsize.width - 4; x += 4 )
                    {
                        int t0 = p0[0], t1 = p0[d0];
                        matH[t0]++; matH[t1]++;
                        p0 += d0*2;
                        t0 = p0[0]; t1 = p0[d0];
                        matH[t0]++; matH[t1]++;
                        p0 += d0*2;
                    }
				}
				 
				//上面的if-else语句执行之后,x的值已经==imsize.width了啊
				//所以下面的循环好像不会被执行的
                for( ; x < imsize.width; x++, p0 += d0 )
				{ 
				    matH[*p0]++;
				}  
            }
            else  //如果该行掩膜矩阵的指针mask不为空,则表示该行内有被禁止统计的
			{	  //像素位置,则该行内的每一个位置在统计前要判断是否被掩盖了,即
				  //mask[x]等于0的位置不予统计
                for( x = 0; x < imsize.width; x++, p0 += d0 )
                    if( mask[x] )
                        matH[*p0]++;
			}
        }
		//将matH中的统计信息根据查找表放入直方图矩阵中
        for(int i = 0; i < 256; i++ )
        { 
			//从查找表取出灰度值为i的像素点对应的直方图存储位置索引
            size_t hidx = tab[i];//一维直方图的情形,所以查找表中有256项,有可能多个i对应于一个hidx
            if( hidx < OUT_OF_RANGE )
                *(int*)(H + hidx) += matH[i];//(int*)(H + hidx)定位到直方图的第hidx个bin处
        }
    }
    else if( dims == 2 )
    {
#ifdef HAVE_TBB
        if( CalcHist2D_8uInvoker::isFit(hist, imsize) )
        {
            callCalcHist2D_8u(_ptrs, _deltas, imsize, hist, dims, _tab);
            return;
        }
#endif
        int d0 = deltas[0], step0 = deltas[1],
            d1 = deltas[2], step1 = deltas[3];
        const uchar* p0 = (const uchar*)ptrs[0];
        const uchar* p1 = (const uchar*)ptrs[1];

        for( ; imsize.height--; p0 += step0, p1 += step1, mask += mstep )
        {
            if( !mask )
			{   //没有掩膜矩阵的情况
                for( x = 0; x < imsize.width; x++, p0 += d0, p1 += d1 )
                {
                    size_t idx = tab[*p0] + tab[*p1 + 256];
                    if( idx < OUT_OF_RANGE )
                        ++*(int*)(H + idx); //二维直方图没有用matH做统计,而是在原来的H上直接累加
                }
			}
            else
			{   //有掩膜矩阵的情况
                for( x = 0; x < imsize.width; x++, p0 += d0, p1 += d1 )
                {
					if(mask[x]) //mask[x]等于0的位置不予统计
					{
						//从查找表取出*p0和*p1对应的直方图存储位置索引
						size_t idx  = tab[*p0] + tab[*p1 + 256] ;//p0是第一维,p1第二维
						if( idx < OUT_OF_RANGE )
							 ++*(int*)(H + idx); //在对应位置上累加直方图
					}
                    
                }
			}
        }
    }
    else if( dims == 3 )
    {
#ifdef HAVE_TBB
        if( CalcHist3D_8uInvoker::isFit(hist, imsize) )
        {
            callCalcHist3D_8u(_ptrs, _deltas, imsize, hist, dims, _tab);
            return;
        }
#endif
        int d0 = deltas[0], step0 = deltas[1],
            d1 = deltas[2], step1 = deltas[3],
            d2 = deltas[4], step2 = deltas[5];

        const uchar* p0 = (const uchar*)ptrs[0];
        const uchar* p1 = (const uchar*)ptrs[1];
        const uchar* p2 = (const uchar*)ptrs[2];

        for( ; imsize.height--; p0 += step0, p1 += step1, p2 += step2, mask += mstep )
        {
            if( !mask )
                for( x = 0; x < imsize.width; x++, p0 += d0, p1 += d1, p2 += d2 )
                {
                    size_t idx = tab[*p0] + tab[*p1 + 256] + tab[*p2 + 512];
                    if( idx < OUT_OF_RANGE )
                        ++*(int*)(H + idx);
                }
            else
                for( x = 0; x < imsize.width; x++, p0 += d0, p1 += d1, p2 += d2 )
                {
                    size_t idx;
                    if( mask[x] && (idx = tab[*p0] + tab[*p1 + 256] + tab[*p2 + 512]) < OUT_OF_RANGE )
                        ++*(int*)(H + idx);
                }
        }
    }
    else
    {
        for( ; imsize.height--; mask += mstep )
        {
            if( !mask )
                for( x = 0; x < imsize.width; x++ )
                {
                    uchar* Hptr = H;
                    int i = 0;
                    for( ; i < dims; i++ )
                    {
                        size_t idx = tab[*ptrs[i] + i*256];
                        if( idx >= OUT_OF_RANGE )
                            break;
                        Hptr += idx;
                        ptrs[i] += deltas[i*2];
                    }

                    if( i == dims )
                        ++*((int*)Hptr);
                    else
                        for( ; i < dims; i++ )
                            ptrs[i] += deltas[i*2];
                }
            else
                for( x = 0; x < imsize.width; x++ )
                {
                    uchar* Hptr = H;
                    int i = 0;
                    if( mask[x] )
                        for( ; i < dims; i++ )
                        {
                            size_t idx = tab[*ptrs[i] + i*256];
                            if( idx >= OUT_OF_RANGE )
                                break;
                            Hptr += idx;
                            ptrs[i] += deltas[i*2];
                        }

                    if( i == dims )
                        ++*((int*)Hptr);
                    else
                        for( ; i < dims; i++ )
                            ptrs[i] += deltas[i*2];
                }
            for(int i = 0; i < dims; i++ )
                ptrs[i] += deltas[i*2 + 1];
        }
    }
}

}

//! 计算给定的一系列图像的联合密度分布直方图(joint dense histogram)
//该函数按照直方图的数据类型和直方图的维数分情况计算直方图
void MyHistFunc::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(); //把InputArray类型的参数转换为Mat类型

	//CV_Assert(...)语句只有Debug模式下会执行,release模式下不执行
    CV_Assert(dims > 0 && histSize);//参数检验,

	//参数_hist是输出参数,保存着计算好的直方图
    uchar* histdata = _hist.getMat().data; //指向直方图的数据部分的指针
    _hist.create(dims, histSize, CV_32F); //为输出直方图开辟空间
    Mat hist = _hist.getMat(), ihist = hist; //把OutputArray类型的参数转换为Mat类型
    ihist.flags = (ihist.flags & ~CV_MAT_TYPE_MASK)|CV_32S;//直方图矩阵的类型信息

	//如果参数_hist中本来就有之前计算的直方图,判断是否重新计算或累加上去
    if( !accumulate || histdata != hist.data )
        hist = Scalar(0.); //清空原来的数据,当前直方图不累加到原来的直方图上去
    else
        hist.convertTo(ihist, CV_32S);//保留原来的直方图,当前计算的结果累加到原来的直方图

	//定义一些中间临时变量
    vector ptrs; //指向图像数据和掩膜矩阵数据的指针数组,有dims+1个指针
    vector deltas;  //存放每一维上的指针移动步长
    vector uniranges;//直方图每一个维度上单位区间内的直方图数目
    Size imsize; //图像尺寸

	//检查掩膜矩阵的有效性,必须是8位单通道
    CV_Assert( !mask.data || mask.type() == CV_8UC1 );

	//前期准备工作,主要是填充前面声明的辅助变量ptrs, deltas, imsize, uniranges 
    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 ) //8位深度
        calcHist_8u(ptrs, deltas, imsize, ihist, dims, ranges, _uniranges, uniform );
    else if( depth == CV_16U )//16位深度
        calcHist_(ptrs, deltas, imsize, ihist, dims, ranges, _uniranges, uniform );
    else if( depth == CV_32F ) //32位深度
        calcHist_(ptrs, deltas, imsize, ihist, dims, ranges, _uniranges, uniform );
    else
        CV_Error(CV_StsUnsupportedFormat, "");

    ihist.convertTo(hist, CV_32F);
}

主函数的实现:(主程序中调用了命名空间MyHistFunc中的函数calcHist(....)

//

#include "stdafx.h"
#include 
#include 
#include "Histogram.h"

using namespace std;
using namespace cv;

Mat image;       //母图像
Mat roi_hist;   //兴趣区域图像块的直方图
Mat roi_patch;  //兴趣区域的图像块
bool selectObject = false; //selectObject的初始值为false,
int calculateHist = 0;  //calculateHist的初值是0
bool showHist = false;
Point origin;
Rect selection;
//用鼠标选择目标图像区域
static void onMouse( int event, int x, int y, int, void* );
//该函数用于计算给定矩形区域的图像块的直方图
static void CalculatePatchHistogram(Rect& patchRect);
//绘制直方图
void DrawHistogramImage();

//设定直方图中bin的数目
int histSize = 256;
//设定取值范围
float range[] = {0,256};//灰度特征的范围都是0到255
const float* histRange = {range};  
bool uniform = true; 
bool accumu = false;//均匀分布,无累加

int _tmain(int argc, _TCHAR* argv[])
{
	string filename = "E:\\素材\\flower1.jpg";
	string winname = "MainWindow";
	namedWindow(winname,1);
	//为 Histogram Demo窗口添加鼠标响应函数,用于选择图像块
	setMouseCallback( "MainWindow", onMouse, 0 );

	Mat src, dst;//声明原始图像和目标图像

	/// 装载图像
    src = imread( filename, 1 );
    if( !src.data ) //判断图像是否成功读取
    { return -1; }

	//src.copyTo(image);
	cvtColor(src,image,CV_BGR2GRAY); 

	for(;;)
	{
		///循环显示读入的图像
		imshow(winname,image);
		if(calculateHist)
		{
			cout<<"开始计算所选区域直方图.... ... ..."<(i-1))),Scalar(0,0,255),1,8,0);
	   */
	   //折线表示
	   line( histImage, Point( bin_w*(i-1), hist_h - cvRound(roi_hist.at(i-1)) ) ,
                      Point( bin_w*(i), hist_h - cvRound(roi_hist.at(i)) ),
                      Scalar( 0, 0, 255), 1, 8, 0  );
	  
   }
    /// 显示直方图
   namedWindow("ROI Hist",1);
   imshow("ROI Hist", histImage );
}

需要看客们注意的地方是:我并没有把TBB下的函数拷贝到自定义的命名空间钟来,因为那是并行运算的代码。

如下所示:

#ifdef HAVE_TBB
calcHist1D_Invoker<T> body(_ptrs, _deltas, hist, _uniranges, size[0], dims, imsize);
parallel_for(BlockedRange(0, imsize.height), body);
return;
#endif
只要没有定义HAVE_TBB标志,自己的命名空间就可以正常运行了,否则会提示你上述类calcHist1D_Invoker<T>没有定义喔





Logo

立足具身智能前沿赛道,致力于搭建全球化、开源化、全栈式技术交流与实践共创平台。

更多推荐