【OpenCV源码阅读】1.1 Mat的结构

#OpenCV源码阅读#

抱着学习的态度,阅读OpenCV源码。我阅读的版本是OpenCV2.4.8,其实2.X在思路上是很相似的。OpenCV自带了详尽的文档。其中:

  • opencv_tutorials.pdf是OpenCV入门文档,该文档中有大量的示例;
  • opencv2refman.pdf是函数手册。

这两个文档是重要的参考资料,使用好两本文档就能使用OpenCV最重要的功能里。

##第一章 Mat##

本章主要内容:Mat类的结构内存管理运算_InputArray类

Mat是OpenCV中最关键的类之一,破解Mat类对把握OpenCV有重要作用,同时通过这部分阅读可以了解OpenCV的设计理念。

Mat是Matrix的简称,中文含义是矩阵,OpenCV2.x摒弃了1.x中区别对待IplImage、CvMat等结构的思路,将所有的问题划归到数学概念中去,图像在线性空间中的表达就是矩阵。

另外,OpenCV中定义了Matx(小矩阵)的结构,Vec(向量)是Matx的子类。他们通过_InputArray(输入数组)类实现相互转换。在矩阵运算、图像处理等函数中,通过以InputArray类为代理,访问Mat或Vec。Mat是Mat的子类,是Mat的模板化。

本章主要涉及文件有:

Path File Description
include/opencv2/core/ core.hpp 其中有Mat类定义
mat.hpp 其中有Mat类实现
types_c.h C语言类型定义和部分函数
modules/core/src/ arithm.cpp 算法实现
matop.cpp Mat的运算

###1.1 Mat的结构###
【core.hpp 1475-2010行】【opencv2refman.pdf 25-44页】

####1.1.1 Mat中成员变量####

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/*! includes several bit-fields:
the magic signature (魔法值)
continuity flag (连续标记)
depth (深度)
number of channels (通道数)
*/
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;
//! pointer to the reference counter;
// when matrix points to user-allocated data, the pointer is NULL
int* refcount;
//! helper fields used in locateROI and adjustROI
uchar* datastart;
uchar* dataend;
uchar* datalimit;
//! custom allocator (自定义分配器)
MatAllocator* allocator;
MSize size;
MStep step;
  • (1)flag 标记,由五部分组成:

flag
1、0-2位 depth 深度,在types_c.h中深度值如下:

10 2
CV_8U 0 0x000
CV_8S 1 0x001
CV_16U 2 0x010
CV_16S 3 0x011
CV_32S 4 0x100
CV_32F 5 0x101
CV_64F 6 0x110
CV_USRTYPE1 7 0x111

2、3-11位 channel 通道数(减1)

灰度图像(1通道)时3-11位是0x000000000,彩色图像(3通道)时3-11位是0x000000010

3、14位:MAT_CONT_FLAG 连续标记

矩阵的数据存储是否连续的标记,1表示连续,0表示不连续

4、15位:SUBMAT_FLAG,子矩阵标记

Mat支持从大矩阵中取出子矩阵,数据没有复制。子矩阵为1,否则为0。

例如,假设Mat A是3×3的矩阵

Matrix A

1
Mat B = A.rowRange(1,2);//B取A的第2到3行,行号以0开始

Matrix B

B的数据并没有新增加,而是直接指向了A的数据,B的datastart和dataend表明了起止位置,rows和cols表明了尺寸。具体将在Mat的内存管理中说明。

5、16-31位:MAGIC_VAL 魔法值,矩阵类型

由于矩阵有很多种类型。图像处理函数中以_InputArray为输入图像,此时就需要依赖这个量来判断数据原始类型。

查看源码可以知道:

类型 说明 MAGIC_VAL值
SparseMat 系数矩阵 0x42FD0000
Mat 矩阵 0x42FF0000
cvMat C矩阵 0x42420000
CvMatND C多维矩阵 0x42430000
CvSparseMat C稀疏矩阵 0x42440000
CvHistogram C直方图 0x42450000
CvMemStorage C内存存储 0x42890000
CvSeq C队列 0x42990000
CvSet C集合 0x42980000
IPLImage IPL图像 0x00000070

他们都是类或结构体的前两个字节(int)。IPLImage来自以往的库,他的前两个字节是int nSize,是结构体的大小,所以他的MAGIC_VAL是0x00000070

  • (2)dims是维度(一般是2),rows是行数,cols是列数

    当维度不为2时,rows=cols=-1

  • (3)data指针指向了数据;refcount指针指向了一个计数器;allocator是动态内存申请器。

    计数器记录着多少个Mat指向了同一块data,当计数为0时,释放data

  • (4)step保存了一行的宽度

    完整连续的图像step.p[0]与cols相等,子矩阵的step.p[0]与原始图像的cols相等。

####1.1.2 Mat的构造函数、赋值运算符、数据类型转换运算符####

构造函数、赋值运算、类型转换都有数据转换能力:

  • (1)构造函数

1、空构造函数【1690行】

2、指定尺寸和类型【1692行】,或由Scalar结构定义尺寸和类型【1696行】

3、高维【1700行】

4、从其他Mat拷贝【1704行】

5、从数据块拷贝并指定尺寸和类型【1706行】

6、只定义一个头,数据指向其他Mat中一部分(行列范围、ROI)【1711行】

7、从老式的CvMat、CvMatND、IplImage转换成Mat【1715-1719行】

8、从新式的std::vector、cv::Vec、cv::Matx、2D point、3D point、comma initializer转换成Mat【1722-1731行】

  • (2)赋值运算

9、const Mat& m从另一个Mat转换【1740行】

10、const MatExpr& expr从矩阵表达式转换成Mat【1741行】

矩阵表达式MatExpr,构造了一个运算结构,具体在Mat的计算中解释

11、const Scalar& s从Scalar转换成Mat,是用一种颜色将整个图像填充【1774行】

  • (3)数据转换运算

12、括号运算符:截取子矩阵【1842-1844行】

13、数据转换:由Mat转换成老式结构CvMat、CvMatND、IplImage【1846-1851行】

14、数据转换:由Mat转换成vector<_tp>、Vec<_tp, n="">、Matx<_tp, m,="" n="">【1853-1855行】