NAryMatIterator源码分析

首先我看了这篇博客 http://blog.csdn.net/fred_yang2013/article/details/11891447 ,讲到了很多细节,但是不完全

接下来我来详细分析一下源码,并且这有助于我们理解Mat的存储方式,真的是花了很大力气看懂这代码,没有注释的代码看的就是蛋疼

首先这就是个Mat的迭代器类,那么我们有疑问了,为什么不直接开数组呢?对,其实完全可以开数组的,但是我们知道Mat有个continuous标志,这表示Mat的数组很可能不是连续存储的,但是我们可以通过公式step[0]*i+step[1]*j+....来访问Mat.data[i][j]...,但是这样我们就是一个个访问矩阵的元素,然而这样效率是不是太低,所以NAryMatIterator这个类采取以一段段连续的空间来访问矩阵(也就是超平面),具体来说我们参考官文档(M.step[i]>=M.step[i+1]*M.size[i+1]),这里为什么会出现大于的情况呢?很显然在第i+1维开始,矩阵并不连续存储了,于是我们的到启发,我们寻找最大的i,满足M.step[i]>M.step[i+1]*M.size[i+1]这个式子,那么可以肯定从i+1维开始到最后一维,都是连续的,所以这就形成了一个超平面,于是我们可以每次读取这么一段超平面。

下面看代码:

class CV_EXPORTS NAryMatIterator
{
public:
    //! the default constructor
    NAryMatIterator();
    //! the full constructor taking arbitrary number of n-dim matrices
    NAryMatIterator(const Mat** arrays, uchar** ptrs, int narrays=-1);
    //! the full constructor taking arbitrary number of n-dim matrices
    NAryMatIterator(const Mat** arrays, Mat* planes, int narrays=-1);
    //! the separate iterator initialization method
    void init(const Mat** arrays, Mat* planes, uchar** ptrs, int narrays=-1);

    //! proceeds to the next plane of every iterated matrix
    NAryMatIterator& operator ++();
    //! proceeds to the next plane of every iterated matrix (postfix increment operator)
    NAryMatIterator operator ++(int);

    //! the iterated arrays
    const Mat** arrays; //注意这个最好以NULL结尾,否则narrays参数要显示传入
    //! the current planes
    Mat* planes; //存每个Mat,其中data域与ptrs相同
    //! data pointers
    uchar** ptrs; //存每个Mat的当前正在访问的超平面的起始地址
    //! the number of arrays
    int narrays; //arrays中Mat的个数
    //! the number of hyper-planes that the iterator steps through
    size_t nplanes; //超平面总个数
    //! the size of each segment (in elements)
    size_t size; //超平面元素数
protected:
    int iterdepth; //这个是最大的i,满足M.step[i]>M.step[i+1]*M.size[i+1]
    size_t idx; //这个是内部使用,记录当前正在访问的超平面是第几个
};

最重要的是init函数

void NAryMatIterator::init(const Mat** _arrays, Mat* _planes, uchar** _ptrs, int _narrays)
{
    CV_Assert( _arrays && (_ptrs || _planes) );
    int i, j, d1=0, i0 = -1, d = -1;
    //初始化
    arrays = _arrays;
    ptrs = _ptrs;
    planes = _planes;
    narrays = _narrays;
    nplanes = 0;
    size = 0;

    if( narrays < 0 ) //该参数默认为-1
    {
        for( i = 0; _arrays[i] != 0; i++ )
            ; //统计Mat的个数,可以看到_arrays以0结束,有点类似字符串
        narrays = i;
        CV_Assert(narrays <= 1000);
    }

    iterdepth = 0;

    //_arrays中的Mat的存储结构必须完全一样
    for( i = 0; i < narrays; i++ )
    {
        CV_Assert(arrays[i] != 0);
        const Mat& A = *arrays[i];
        if( ptrs )
            ptrs[i] = A.data;

        if( !A.data )
            continue;

        if( i0 < 0 )
        {
            i0 = i; //i0记录第一个A.data!=null的Mat的id
            d = A.dims; //Mat的维数

            // find the first dimensionality which is different from 1;
            // in any of the arrays the first "d1" step do not affect the continuity
            for( d1 = 0; d1 < d; d1++ ) //d1记录最先出现大于1的维度
                if( A.size[d1] > 1 )
                    break;
        }
        else //对之后的矩阵必须要与i0对应的矩阵具有完全相同结构
            CV_Assert( A.size == arrays[i0]->size );

        if( !A.isContinuous() )
        {
            //不连续储存的情况
            CV_Assert( A.step[d-1] == A.elemSize() );
            for( j = d-1; j > d1; j-- ) //第一次出现该不等式的j
                if( A.step[j]*A.size[j] < A.step[j-1] )
                    break;
            iterdepth = std::max(iterdepth, j); //iterdepth记录所有j的最大值
        }
    }

    if( i0 >= 0 ) //有Mat的data不为空的情况
    {
        size = arrays[i0]->size[d-1]; //最后一维的size
        for( j = d-1; j > iterdepth; j-- ) //从iterdepth维->d-1维组成了超平面
        {
            int64 total1 = (int64)size*arrays[i0]->size[j-1];
            if( total1 != (int)total1 ) //totall是否爆64位的范围
                break;
            size = (int)total1;
        }
        //执行到这儿,size是超平面的元素数
        iterdepth = j;
        if( iterdepth == d1 )
            iterdepth = 0;

        nplanes = 1;
        //从0维->iterdepth-1维组成了超平面个数
        for( j = iterdepth-1; j >= 0; j-- )
            nplanes *= arrays[i0]->size[j];
        //执行到这儿,nplanes是超平面的个数
    }
    else
        iterdepth = 0;

    idx = 0;

    if( !planes )
        return;

    for( i = 0; i < narrays; i++ )
    {
        CV_Assert(arrays[i] != 0);
        const Mat& A = *arrays[i];

        if( !A.data )
        {
            planes[i] = Mat();
            continue;
        }
        //存储每个Mat的第一个超平面
        planes[i] = Mat(1, (int)size, A.type(), A.data);
    }
}

三个构造函数就很自然了

NAryMatIterator::NAryMatIterator()
    : arrays(0), planes(0), ptrs(0), narrays(0), nplanes(0), size(0), iterdepth(0), idx(0)
{
}

NAryMatIterator::NAryMatIterator(const Mat** _arrays, Mat* _planes, int _narrays)
: arrays(0), planes(0), ptrs(0), narrays(0), nplanes(0), size(0), iterdepth(0), idx(0)
{
    init(_arrays, _planes, 0, _narrays);
}

NAryMatIterator::NAryMatIterator(const Mat** _arrays, uchar** _ptrs, int _narrays)
    : arrays(0), planes(0), ptrs(0), narrays(0), nplanes(0), size(0), iterdepth(0), idx(0)
{
    init(_arrays, 0, _ptrs, _narrays);
}

前置自增运算

//将迭代器指向所有Mat的下一个超平面
NAryMatIterator& NAryMatIterator::operator ++()
{
    if( idx >= nplanes-1 ) //当所有超平面都遍历完结束
        return *this;
    ++idx; //遍历下一个超平面

    if( iterdepth == 1 )
    {
        //有size[0]个超平面的情况
        if( ptrs )
        {
            for( int i = 0; i < narrays; i++ )
            {
                if( !ptrs[i] )
                    continue;
                //每次用step[0]*idx来定位第idx个超平面
                ptrs[i] = arrays[i]->data + arrays[i]->step[0]*idx;
            }
        }
        if( planes )
        {
            for( int i = 0; i < narrays; i++ )
            {
                if( !planes[i].data )
                    continue;
                //每次用step[0]*idx来定位第idx个超平面
                planes[i].data = arrays[i]->data + arrays[i]->step[0]*idx;
            }
        }
    }
    else
    {
        //有size[0]*size[1]*...*size[iterdepth-1]个超平面
        for( int i = 0; i < narrays; i++ )
        {
            const Mat& A = *arrays[i];
            if( !A.data )
                continue;
            int _idx = (int)idx;
            uchar* data = A.data;
            //_idx=k0*size[0]+k1*size[1]*...
            //我们需要把k0,k1,...提取出来,就是下面的操作,不停的%和/
            for( int j = iterdepth-1; j >= 0 && _idx > 0; j-- )
            {
                int szi = A.size[j], t = _idx/szi;
                //(_idx - t * szi)等价于_idx%szi,表示在j维有多少个超平面
                data += (_idx - t * szi)*A.step[j];
                _idx = t; //等价于_idx/szi
            }
            if( ptrs )
                ptrs[i] = data;
            if( planes )
                planes[i].data = data;
        }
    }

    return *this;
}

后置自增就很简单了

NAryMatIterator NAryMatIterator::operator ++(int)
{
    NAryMatIterator it = *this;
    ++*this;
    return it;
}

有了这个东西,我们就可以很方便遍历Mat数组,以超平面的形式...


  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值