OpenCV opencv 中 Mat 类中成员变量 flags 详解

sumu · 2019年08月22日 · 133 次阅读

opencv中Mat类中成员变量flags详解

1 背景

最近在看opencv的源码,首先就是最最常用的容器Mat类,其中Mat类一共有12个成员变量(也叫作成员属性),而flags就是其中的一个,那今天我们就去详细的看一看flags相关的定义与实现。 我们先列出这12个成员变量。

int flags;
//! the matrix dimensionality, >= 2
int dims;
//! the number of rows and columns or (-1, -1) when the matrix has more than 2 dimensions
int rows, cols;
//! pointer to the data
uchar* data;

//! helper fields used in locateROI and adjustROI
const uchar* datastart;
const uchar* dataend;
const uchar* datalimit;

//! custom allocator
MatAllocator* allocator;

//! interaction with UMat
UMatData* u;

MatSize size;
MatStep step;

2 源码分析

2.1 depth

可以很明显的看到flagsint类型。总长度也就是就是32位。见名知意,flags是一个标志,它表征着Mat的某些特征。我们可以通过注释看到它标志了哪些信息。如下

/*! includes several bit-fields:
         - the magic signature
         - continuity flag
         - depth
         - number of channels
     */

看到这里你就会明白flags原来标志着这些东西,那为什么不通过多设置几个成员变量来实现呢,这就体现出了opencv的设计之处,首先可以通过增加成员变量来实现,但是就像depth,一共只有8中类型,换成二进制表示也就仅仅需要3bit。如果用一字节存储还有点浪费呢,这个时候你肯定会说,现在电脑内存这么大还在乎那点内存,这个不仅仅是浪费的问题,更主要的是需要多次分配小内存,影响执行效率。所以设计人员使用了一个int类型的flags来存储相关信息。但是这样带来的就是我只能读取flags的值,还需要进一步提取所需要的信息。 比如现在我需要获取depth,也就是图像的存储深度,我该怎么办呢?opencv已经提供了depth()方法来获取depth。让我们详细看一下depth()的实现原理。

inline
int Mat::depth() const
{
    return CV_MAT_DEPTH(flags);
}

这是一个inline函数,设计人员时时刻刻都在想着提升opencv的执行速度,不懂inline的小伙伴大家可以去看看inline关键字的作用。(简单的说一下就是,普通函数为了代码的可重用性,程序执行时会去调用,而inline函数在编译时,需要调用的地方就会编译不再去调用执行,加快了执行速度。以空间换时间的方式。)很明显就是返回了depth。可是那个宏又是怎么回事呢?我们接着看,看一看CV_MAT_DEPTH(flags),到底是什么?

#define CV_CN_SHIFT   3
#define CV_DEPTH_MAX  (1 << CV_CN_SHIFT)
#define CV_MAT_DEPTH_MASK       (CV_DEPTH_MAX - 1)
#define CV_MAT_DEPTH(flags)     ((flags) & CV_MAT_DEPTH_MASK)

首先看CV_CN_SHIFT常量为3CV_DEPTH_MAX1左移CV_CN_SHIFT次也就是3次(二进制的0001->1000),所以CV_DEPTH_MAX8。此时CV_MAT_DEPTH_MASKCV_DEPTH_MAX - 1,也就是为7(二进制的111),那这样就很清楚了原来是通过flagsCV_MAT_DEPTH_MASK按位与,来获取低三位数据信息。flags = 0b xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx CV_MAT_DEPTH_MASK = 0b 0000 0000 0000 0000 0000 0000 0000 0111这样把其它位置0,仅仅获取低三位,也就是depth,所以depth最多只能存储8种类型。

2.2 channels

那我们看一看number of channels怎么获取,这个也是通过inline函数channals()来获取,让我们看一看源码。

inline
int Mat::channels() const
{
    return CV_MAT_CN(flags);
}

那我们在看一看这个宏是怎么定义的CV_MAT_CN(flags)

#define CV_MAT_CN(flags)        ((((flags) & CV_MAT_CN_MASK) >> CV_CN_SHIFT) + 1)
#define CV_MAT_CN_MASK          ((CV_CN_MAX - 1) << CV_CN_SHIFT)
#define CV_CN_MAX     512

首先可以看出来opencv中最大通道数CV_CN_MAX定义为512,此时CV_MAT_CN_MASK511(512-1)左移CV_CN_SHIFT也就是左移3位。这个怎么理解呢,大家还记不记得刚才低三位的MASK7也就是0b111,所以想要表示最大值为512的数据需要9位(有人这里会疑惑512不应该是10位嘛,因为通道数有实际意义所以为1~512对应0-511,所以9位即可存储不会溢出),所以此时的MASK应该为0b_1_1111_1111 但是channelsdepth同存储在flags,占用不同位域(bit-fields),所以应该用左移3位,将低三位留给depth使用。同理,想要获取3-11位的channels数据只需要将flagsCV_MAT_CN_MASK做按位与操作然后再右移3位即可得到,因为没有0通道,而计算机可是从0开始计数,所以再加1,即可得到number of channels。 同理,可以通过成员函数type()来获取Mat的类型,这个我就不再详细介绍,因为就是depth和channels的结合,depth占据0-2位,channels占据3-11位,而type占0-11位。

2.3 isContinuous

flags中的第14位,让我们看一下是怎么实现的。

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

依然是一个inline的常函数(就是const修饰的,函数体内不能修改参数值),因为是这个标志了Mat对象在内存中是否连续。因此仅仅需要一位就可以表示。我们可以追根溯源,看到CONTINUOUS_FLAG = CV_MAT_CONT_FLAG我们继续探索,可以看到如下宏定义,

#define CV_MAT_CONT_FLAG_SHIFT  14
#define CV_MAT_CONT_FLAG        (1 << CV_MAT_CONT_FLAG_SHIFT)

可以看到CV_MAT_CONT_FLAG就是1<<14也就是0b_100_0000_0000_0000就是通过flags与0b_100_0000_0000_0000按位与如果第14位为1则返回true,否则返回false

2.4 isSubmatrix

flags中的第15位,让我们看一下是怎么实现的。

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

依然是一个inline的常函数(就是const修饰的,函数体内不能修改参数值),因为是这个标志了Mat对象是否是其它Mat对象的submatrix。因此仅仅需要一位就可以表示。我们可以追根溯源,看到SUBMATRIX_FLAG = CV_SUBMAT_FLAG我们继续探索,可以看到如下宏定义,

#define CV_SUBMAT_FLAG_SHIFT    15
#define CV_SUBMAT_FLAG          (1 << CV_SUBMAT_FLAG_SHIFT)

可以看到CV_SUBMAT_FLAG就是1<<15也就是0b_1000_0000_0000_0000就是通过flags与0b_1000_0000_0000_0000按位与如果第15位为1则返回true,否则返回false

2.5 the magic signature

flags中的第15位~第31位,来表示the magic signature。这部分目前相关资料也比较少,只能通过源码来进行判断。目前根据opencv 4.1.1中确定容器类型为Mat和SparseMat(3~10都是opencv 1x 版本中的),所以现在MAGIC_VAL取值为:

序号 类型 MAGIC_VAL
1 SparseMat 0x42FD0000
2 Mat 0x42FF0000
3 cvMat 0x42420000
4 CvMatND 0x42430000
5 CvSparseMat 0x42440000
6 CvHistogram 0x42450000
7 CvMemStorage 0x42890000
8 CvSeq 0x42990000
9 CvSet 0x42980000
10 IPLImage 0x00000070

回顾一下

我们主要介绍了flags中各个位所表示的含义,flags为int类型,32位。其中由低到高排序,0-2位代表了depth,可以通过depth()来获取。3-11位代表了channels,可以通过channels()来获取,12、13位暂无意义,14位代表了Mat类对象内存是否连续,可以通过其成员函数isContinuous()获取,15位代表了Mat对象是否是其它Mat对象的一部分(submatrix)可以通过成员函数isSubmatrix()获取 。16-31位代表了the magic signature,反映了现在数据的类型比如是矩阵还是稀疏矩阵或者由老版本的IplImage转化来的矩阵等。

总览如图所示:

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册