Opencv3 core模块解析之Mat类(1)

在阅读opencv文档和源码时,常常看到InputArray、OutputArray、UMat、Mat_等“奇怪”的类,与我们常用的Mat,好像一样又不完全一样,这些其实主要在opencv内部用,其中有些文档都很少甚至没有,在opencv外部程序还是Mat主打。另外还有Mat的成员create(),copyTo(),clone(),data,empty()等,到底有哪些使用中的注意事项。本文通过源码解析一下。

代理类(proxy)InputArray、OutputArray作为函数的形参,统一接口。opencv用户比较少用到,但是很多内部函数都用来做形参,确实,这两个类只用作形参,不要用来声明新的变量。可以传进来的类包括:

MatMat_<T>Matx<T, m, n>std::vector<T>std::vector<std::vector<T> >std::vector<Mat>std::vector<Mat_<T> >UMatstd::vector<UMat> or double。

  • 问题:InputArray、OutputArray有何区别?

两者的关系,InputArray是只读的,相当于const cv::Mat&,OutputArray是可读写的,对应cv::Mat &,一般函数返回之前,如果已经分配了大小类型合适的内存就接着用,否则就create分配空间,这时候需要改写cv::Mat的地址,所以是非常量的引用。InputArray有kind(),getMat()这些方法。一般的操作前,会getMat然后用Mat来编程。可以把InputArray、OutputArray理解成一个菜篮子,只要是菜都能放到篮子里,菜在用的时候,分门别类来用。

mat.inl.hpp

inline _InputArray::_InputArray(const Mat& m) 
{ init(MAT+ACCESS_READ, &m); }
​​​​​​​inline _OutputArray::_OutputArray(const Mat& m)
{ init(FIXED_TYPE + FIXED_SIZE + MAT + ACCESS_WRITE, &m); }


inline void _InputArray::init(int _flags, const void* _obj)
{ flags = _flags; obj = (void*)_obj; }

inline
bool Mat::empty() const
{
    return data == 0 || total() == 0 || dims == 0;
}
  • 问题:empty()是怎么判断空Mat?

指针是0,这种情况肯定空,因为0这个地址不能分配。total或dim是0也是空。

  • 问题:data怎么理解

Mat的public成员uchar*  data。2维的图片,是一行一行排列的,在遍历时,应该是

for(int i=0;i<rows;i++)

     .... for(int j=0;j<cols;j++)

i,j的顺序来自c语言的传统。

 这里uchar只是个名义上的类型,具体访问这个指针都要转为type对应的类型。this->data通过Mat的at索引读写数据。

Mat的data内存有时不连续,这通常发生在选区之后,读内存要用到step(step>=cols),也就是一行后可能要跳过一段再是下一行。

  • 问题:create()能不能避免重复分配内存?

答案:是可以的,如果this已经分配了空间,create就不重复分配;如果改了类型或通道数,重分配之前会释放,所以,放心食用。2维Mat如果col=1,data等效于1维Mat。

Mat用了引用计数。

matrix.cpp

void Mat::create(int d, const int* _sizes, int _type)
{
    int i;
    CV_Assert(0 <= d && d <= CV_MAX_DIM && _sizes);
    _type = CV_MAT_TYPE(_type);

    if( data && (d == dims || (d == 1 && dims <= 2)) && _type == type() )
    {
        if( d == 2 && rows == _sizes[0] && cols == _sizes[1] )
            return;
        for( i = 0; i < d; i++ )
            if( size[i] != _sizes[i] )
                break;
        if( i == d && (d > 1 || size[1] == 1))
            return;
    }

    int _sizes_backup[CV_MAX_DIM]; // #5991
    if (_sizes == (this->size.p))
    {
        for(i = 0; i < d; i++ )
            _sizes_backup[i] = _sizes[i];
        _sizes = _sizes_backup;
    }

    release();
    if( d == 0 )
        return;
    flags = (_type & CV_MAT_TYPE_MASK) | MAGIC_VAL;
    setSize(*this, d, _sizes, 0, true);

    if( total() > 0 )
    {
        MatAllocator *a = allocator, *a0 = getDefaultAllocator();
#ifdef HAVE_TGPU
        if( !a || a == tegra::getAllocator() )
            a = tegra::getAllocator(d, _sizes, _type);
#endif
        if(!a)
            a = a0;
        try
        {
            u = a->allocate(dims, size, _type, 0, step.p, 0, USAGE_DEFAULT);
            CV_Assert(u != 0);
        }
        catch (...)
        {
            if (a == a0)
                throw;
            u = a0->allocate(dims, size, _type, 0, step.p, 0, USAGE_DEFAULT);
            CV_Assert(u != 0);
        }
        CV_Assert( step[dims-1] == (size_t)CV_ELEM_SIZE(flags) );
    }

    addref();
    finalizeHdr(*this);
}

 copy.cpp

void Mat::copyTo( OutputArray _dst ) const
{
    CV_INSTRUMENT_REGION();

#ifdef HAVE_CUDA
    if (_dst.isGpuMat())
    {
        _dst.getGpuMat().upload(*this);
        return;
    }
#endif

    int dtype = _dst.type();
    if( _dst.fixedType() && dtype != type() )
    {
        CV_Assert( channels() == CV_MAT_CN(dtype) );
        convertTo( _dst, dtype );
        return;
    }

    if( empty() )
    {
        _dst.release();
        return;
    }

    if( _dst.isUMat() )
    {
        _dst.create( dims, size.p, type() );
        UMat dst = _dst.getUMat();
        CV_Assert(dst.u != NULL);
        size_t i, sz[CV_MAX_DIM] = {0}, dstofs[CV_MAX_DIM], esz = elemSize();
        CV_Assert(dims > 0 && dims < CV_MAX_DIM);
        for( i = 0; i < (size_t)dims; i++ )
            sz[i] = size.p[i];
        sz[dims-1] *= esz;
        dst.ndoffset(dstofs);
        dstofs[dims-1] *= esz;
        dst.u->currAllocator->upload(dst.u, data, dims, sz, dstofs, dst.step.p, step.p);
        return;
    }

    if( dims <= 2 )
    {
        _dst.create( rows, cols, type() );
        Mat dst = _dst.getMat();
        if( data == dst.data )
            return;

        if( rows > 0 && cols > 0 )
        {
            Mat src = *this;
            Size sz = getContinuousSize2D(src, dst, (int)elemSize());
            CV_CheckGE(sz.width, 0, "");

            const uchar* sptr = src.data;
            uchar* dptr = dst.data;

#if IPP_VERSION_X100 >= 201700
            CV_IPP_RUN_FAST(CV_INSTRUMENT_FUN_IPP(ippiCopy_8u_C1R_L, sptr, (int)src.step, dptr, (int)dst.step, ippiSizeL(sz.width, sz.height)) >= 0)
#endif

            for (; sz.height--; sptr += src.step, dptr += dst.step)
                memcpy(dptr, sptr, sz.width);
        }
        return;
    }

    _dst.create( dims, size, type() );
    Mat dst = _dst.getMat();
    if( data == dst.data )
        return;

    if( total() != 0 )
    {
        const Mat* arrays[] = { this, &dst };
        uchar* ptrs[2] = {};
        NAryMatIterator it(arrays, ptrs, 2);
        size_t sz = it.size*elemSize();

        for( size_t i = 0; i < it.nplanes; i++, ++it )
            memcpy(ptrs[1], ptrs[0], sz);
    }
}

inline
Mat Mat::clone() const
{
    Mat m;
    copyTo(m);
    return m;
}

matrix_wrap.cpp 

bool _OutputArray::fixedType() const
{
    return (flags & FIXED_TYPE) == FIXED_TYPE;
}

clone和copyTo基本相同,只是CopyTo类型更广一些。新分配的内存保证是连续的。

template<typename _Tp> class Mat_ : public Mat

Mat_的行为有些像Matlab的矩阵,想要更清晰显示类型,和按位置处理数据时可以用。

这几个成员方法基本不会内存出错,但是copyTo有一个情况要注意,当dst是const Mat&时,需要事先分配好内存,大小类型要和src一致。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值