Bootstrap

Open Cv Matrix 详解

Matrix的类型

enum { MAGIC_VAL  = 0x42FF0000, AUTO_STEP = 0, CONTINUOUS_FLAG = CV_MAT_CONT_FLAG, SUBMATRIX_FLAG = CV_SUBMAT_FLAG };
    enum { MAGIC_MASK = 0xFFFF0000, TYPE_MASK = 0x00000FFF, DEPTH_MASK = 7 };
/*! includes several bit-fields:
         - the magic signature
         - continuity flag
         - depth
         - number of channels
     */
    int flags;

从定义可以看出flags是int类型,共占32位,包含信息the magic signature,continuity flag,depth,number of channels。

/****************************************************************************************\
*                                  Matrix type (Mat)                                     *
\****************************************************************************************/

#define CV_MAT_CN_MASK          ((CV_CN_MAX - 1) << CV_CN_SHIFT)
#define CV_MAT_CN(flags)        ((((flags) & CV_MAT_CN_MASK) >> CV_CN_SHIFT) + 1)
#define CV_MAT_TYPE_MASK        (CV_DEPTH_MAX*CV_CN_MAX - 1)
#define CV_MAT_TYPE(flags)      ((flags) & CV_MAT_TYPE_MASK)
#define CV_MAT_CONT_FLAG_SHIFT  14
#define CV_MAT_CONT_FLAG        (1 << CV_MAT_CONT_FLAG_SHIFT)
#define CV_IS_MAT_CONT(flags)   ((flags) & CV_MAT_CONT_FLAG)
#define CV_IS_CONT_MAT          CV_IS_MAT_CONT
#define CV_SUBMAT_FLAG_SHIFT    15
#define CV_SUBMAT_FLAG          (1 << CV_SUBMAT_FLAG_SHIFT)
#define CV_IS_SUBMAT(flags)     ((flags) & CV_MAT_SUBMAT_FLAG)

/** Size of each channel item,
这个数值对应图像深度
							16F   64F  32F   32S  16S    16U  8S    8U
   0x28442211 = 0010 1000 0100 0100 0010 0010 0001 0001 ~ array of sizeof(arr_type_elem) */
#define CV_ELEM_SIZE1(type) ((0x28442211 >> CV_MAT_DEPTH(type)*4) & 15)

#define CV_ELEM_SIZE(type) (CV_MAT_CN(type)*CV_ELEM_SIZE1(type))

depth信息

DEPTH_MASK = 7 ,最后3位是深度信息,Depth总共只有8个类型,定义如下,定义了单个通道单个像素的值的类型。

#define CV_CN_MAX     512
#define CV_CN_SHIFT   3
#define CV_DEPTH_MAX  (1 << CV_CN_SHIFT)

#define CV_8U   0
#define CV_8S   1
#define CV_16U  2
#define CV_16S  3
#define CV_32S  4
#define CV_32F  5
#define CV_64F  6
#define CV_16F  7

#define CV_MAT_DEPTH_MASK       (CV_DEPTH_MAX - 1)
#define CV_MAT_DEPTH(flags)     ((flags) & CV_MAT_DEPTH_MASK)

#define CV_MAKETYPE(depth,cn) (CV_MAT_DEPTH(depth) + (((cn)-1) << CV_CN_SHIFT))
#define CV_MAKE_TYPE CV_MAKETYPE

#define CV_8UC1 CV_MAKETYPE(CV_8U,1)
#define CV_8UC2 CV_MAKETYPE(CV_8U,2)
#define CV_8UC3 CV_MAKETYPE(CV_8U,3)
#define CV_8UC4 CV_MAKETYPE(CV_8U,4)
#define CV_8UC(n) CV_MAKETYPE(CV_8U,(n))

#define CV_8SC1 CV_MAKETYPE(CV_8S,1)
#define CV_8SC2 CV_MAKETYPE(CV_8S,2)
#define CV_8SC3 CV_MAKETYPE(CV_8S,3)
#define CV_8SC4 CV_MAKETYPE(CV_8S,4)
#define CV_8SC(n) CV_MAKETYPE(CV_8S,(n))

#define CV_16UC1 CV_MAKETYPE(CV_16U,1)
#define CV_16UC2 CV_MAKETYPE(CV_16U,2)
#define CV_16UC3 CV_MAKETYPE(CV_16U,3)
#define CV_16UC4 CV_MAKETYPE(CV_16U,4)
#define CV_16UC(n) CV_MAKETYPE(CV_16U,(n))

#define CV_16SC1 CV_MAKETYPE(CV_16S,1)
#define CV_16SC2 CV_MAKETYPE(CV_16S,2)
#define CV_16SC3 CV_MAKETYPE(CV_16S,3)
#define CV_16SC4 CV_MAKETYPE(CV_16S,4)
#define CV_16SC(n) CV_MAKETYPE(CV_16S,(n))

#define CV_32SC1 CV_MAKETYPE(CV_32S,1)
#define CV_32SC2 CV_MAKETYPE(CV_32S,2)
#define CV_32SC3 CV_MAKETYPE(CV_32S,3)
#define CV_32SC4 CV_MAKETYPE(CV_32S,4)
#define CV_32SC(n) CV_MAKETYPE(CV_32S,(n))

#define CV_32FC1 CV_MAKETYPE(CV_32F,1)
#define CV_32FC2 CV_MAKETYPE(CV_32F,2)
#define CV_32FC3 CV_MAKETYPE(CV_32F,3)
#define CV_32FC4 CV_MAKETYPE(CV_32F,4)
#define CV_32FC(n) CV_MAKETYPE(CV_32F,(n))

#define CV_64FC1 CV_MAKETYPE(CV_64F,1)
#define CV_64FC2 CV_MAKETYPE(CV_64F,2)
#define CV_64FC3 CV_MAKETYPE(CV_64F,3)
#define CV_64FC4 CV_MAKETYPE(CV_64F,4)
#define CV_64FC(n) CV_MAKETYPE(CV_64F,(n))

#define CV_16FC1 CV_MAKETYPE(CV_16F,1)
#define CV_16FC2 CV_MAKETYPE(CV_16F,2)
#define CV_16FC3 CV_MAKETYPE(CV_16F,3)
#define CV_16FC4 CV_MAKETYPE(CV_16F,4)
#define CV_16FC(n) CV_MAKETYPE(CV_16F,(n))

Chanels信息

TYPE_MASK = 0x00000FFF,类型总共12位,除了最后三位深度信息,其余都是chanels,总共9位,也就是最大支持512

continuity flag

#define CV_MAT_CONT_FLAG_SHIFT 14
#define CV_MAT_CONT_FLAG (1 << CV_MAT_CONT_FLAG_SHIFT)
#define CV_IS_MAT_CONT(flags) ((flags) & CV_MAT_CONT_FLAG)
第15位代表Mat的内存是否连续,一般由creat创建的mat均是连续的,如果是连续,将加快对数据的访问。

Submatrix

#define CV_SUBMAT_FLAG_SHIFT 15
#define CV_SUBMAT_FLAG (1 << CV_SUBMAT_FLAG_SHIFT)
#define CV_IS_SUBMAT(flags) ((flags) & CV_MAT_SUBMAT_FLAG)
第16位代表该Mat是否为某一个Mat的submatrix,一般通过ROI以及row()、col()、rowRange()、colRange()等得到的mat均为submatrix。

magic

MAGIC_MASK = 0xFFFF0000,16-31代表magic signature,暂理解为用来区分Mat的类型

inline
bool Mat::isContinuous() const
{
    return (flags & CONTINUOUS_FLAG) != 0;
}
inline
bool Mat::isSubmatrix() const
{
    return (flags & SUBMATRIX_FLAG) != 0;
}

总结

从低位到高位:

0-2位代表depth即数据类型(如CV_8U),OpenCV的数据类型共7类,故只需3位即可全部表示。

3-11位代表通道数channels,因为OpenCV默认最大通道数为512,故只需要9位即可全部表示,可参照下面求通道数的部分。

0-11位共同代表type即通道数和数据类型(如CV_8UC3)

12-13位暂没发现用处,也许是留着后用,待发现了再补上。

14位代表Mat的内存是否连续,一般由creat创建的mat均是连续的,如果是连续,将加快对数据的访问。

15位代表该Mat是否为某一个Mat的submatrix,一般通过ROI以及row()、col()、rowRange()、colRange()等得到的mat均为submatrix。

16-31代表magic signature,暂理解为用来区分Mat的类型,如果Mat和SparseMat

mat创建

mat的初始化方法有很多,最后都是通过create创建的内存块

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, ACCESS_RW /* ignored */, USAGE_DEFAULT);//分配内存
           CV_Assert(u != 0);
       }
       catch (...)
       {
           if (a == a0)
               throw;
           u = a0->allocate(dims, size, _type, 0, step.p, ACCESS_RW /* ignored */, USAGE_DEFAULT);//如果设置的分配器分配失败会使用默认的分配器尝试一次分配。如果再次异常,不会处理异常
           CV_Assert(u != 0);
       }
       CV_Assert( step[dims-1] == (size_t)CV_ELEM_SIZE(flags) );//判断分配的内存大小是不是对的。
   }

   addref();//给分配的内存添加一个引用计数
   finalizeHdr(*this);
}

mat初始化的时候都会在初始化列表将所有成员初始化为0,默认构造函数因为没有参数,所有没有使用create生成mat,其余的一半都会传入一个可以确定mat的维度和每个维度大小也就是矩阵大小的参数,
从CV_MAX_DIM 可以看出来这个矩阵最多有32维。还有一个每个元素大小的type值,也就是通道和颜色深度。

创建新的内存之前会先尝试看是不是需要重新分配,减少操作,一般新建的对象都是需要分配的,这里主要讲分配,不具体分析什么情况不会重新分配。
分配之前会使用release, CV_XADD(&u->refcount, -1),会通过_InterlockedExchangeAdd函数进行支持多线程的数据原子操作,修改指针引用计数,一旦引用为0就会自动释放内存,然后把指针置为空,正常来说,多次调用release是没有问题的,但是有时候,new的mat对象delete之后会有异常,不知道是不是release还有其他问题,所以尽量不要是要new delete操作处理mat对象,我这里是使用shared ptr让系统维护内存,然后就没有问题了。

inline
void Mat::release()
{
    if( u && CV_XADD(&u->refcount, -1) == 1 )
        deallocate();
    u = NULL;
    datastart = dataend = datalimit = data = 0;
    for(int i = 0; i < dims; i++)
        size.p[i] = 0;
#ifdef _DEBUG
    flags = MAGIC_VAL;
    dims = rows = cols = 0;
    if(step.p != step.buf)
    {
        fastFree(step.p);
        step.p = step.buf;
        size.p = &rows;
    }
#endif
}

释放掉之前的内存之后,会设置一下和图片大小相关的一些参数,因为后面需要使用这些参数分配内存。系统有一个默认的内存分配器,mat的构造函数允许你指定分配器,如果你不指定就使用默认的,同样的会对你指定的分配器分配失败之后,抓取异常,重新使用默认的分配器分配一次。

默认的构造是StdMatAllocator ,并且不能再被派生。
class StdMatAllocator CV_FINAL : public MatAllocator,下面是分配的函数

UMatData* allocate(int dims, const int* sizes, int type,
                       void* data0, size_t* step, AccessFlag /*flags*/, UMatUsageFlags /*usageFlags*/) const CV_OVERRIDE
    {
        size_t total = CV_ELEM_SIZE(type);
        for( int i = dims-1; i >= 0; i-- )
        {
            if( step )
            {
                if( data0 && step[i] != CV_AUTOSTEP )
                {
                    CV_Assert(total <= step[i]);
                    total = step[i];
                }
                else
                    step[i] = total;
            }
            total *= sizes[i];
        }
        uchar* data = data0 ? (uchar*)data0 : (uchar*)fastMalloc(total);
        UMatData* u = new UMatData(this);
        u->data = u->origdata = data;
        u->size = total;
        if(data0)
            u->flags |= UMatData::USER_ALLOCATED;

        return u;
    }

可以看到这个分配函数实际上只是算了需要分配的内存的大小,然后将这个内存放到UMatData的date段。这里fastMalloc是直接使用的malloc,然后就是针对不同平台做了些微调。
以上就是mat矩阵的一些分析,后面有时间再补。

;