今天学习core模块最重要的一个结构:Mat。Mat作为OpenCV核心的数据结构,是2.x版本后的数字图像存储的主要承担着。当然Mat类的内容也相当复杂,下面我们会以功能性分块介绍Mat类。
今天我打算从5个方面学习Mat结构。
(1) 基本介绍
(2)Mat类的整体结构
(3)Mat对象的初始化
1. 通过构造函数初始化
2. 通过非构造函数初始化
(4)Mat对象内像素数据的获取
1. 了解对象内部数据部分的指针方式读取
2. 多维空间像素数据读取原理
(5)Mat类内部常用的成员函数和属性
1. 常用属性
2. 常用成员函数
Mat类用来体现一个n维的单通道或多通道的稠密的数字向量。Mat可以存储实数或复数域的向量,矩阵,灰度级,彩色图,像素, 向量场,点云,张量,直方图。Mat结构中的数据完全兼容于OpenCV 1.x中的 CvMat, IplImage,和CvMatND类型的数据。同时Mat也兼容主要的标准工具包和集成开发环境中的稠密向量(dense array)类型数据,比如Numpy,Win32。总的来说就是Mat这个类的功能很强大,可以存储多维向量和矩阵,同样兼容和其存储原理相同的其他工具包的数据类型。而在OpenCV中我们最常用到的就是用Mat类存储数字图像。并且使用非常方便。前面第二篇文章中我介绍到OpenCV的特性的时候说到过自动化内存管理的特性,这里Mat类显然具有这一特性。Mat结构具有其构析函数,构析函数在需要的时候会释放深层的缓冲区。但是在条件不到的情况下不会释放缓冲区,一直保留缓冲区的数据。其实这样做的目的是为了节省内存空间。这样做其实考虑了数据部分的共享问题,在一个对象被建立的时候会相应的建立数据部分和数据头部,数据的头部用来存储本对象的特征信息,数据部分作为共享,每当引用一次数据部分则引用计数会+1,相应的当且仅当引用计数达到0时数据部分才会被释放。基于这种特性,Mat类的一些操作几乎是不花费时间就可操作的,因为那些操作仅仅只对对象的头部进行了相应的更改,而数据部分却不会受到影响,数据头在整个对象中占的比例又非常小所以操作数据头部的时间复杂度要远远小于操作数据部分。
class CV_EXPORTS Mat
{
public:
// ... a lot of methods ...
...
/*! includes several bit-fields:
- the magic signature
- continuity flag
- depth
- number of channels
*/
int flags;
//! the array dimensionality, >= 2
int dims;
//! the number of rows and columns or (-1, -1) when the array has more than 2 dimensions
int rows, cols;
//! pointer to the data
uchar* data;
//! pointer to the reference counter;
// when array points to user-allocated data, the pointer is NULL
int* refcount;
// other members
...
};
1 C++: Mat::Mat()
2 C++: Mat::Mat(int rows, int cols, int type)
3 C++: Mat::Mat(Size size, int type)
4 C++: Mat::Mat(int rows, int cols, int type, const Scalar& s)
5 C++: Mat::Mat(Size size, int type, const Scalar& s)
6 C++: Mat::Mat(const Mat& m)
7 C++: Mat::Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP)
8 C++: Mat::Mat(Size size, int type, void* data, size_t step=AUTO_STEP)
9 C++: Mat::Mat(const Mat& m, const Range& rowRange, const Range& colRange=Range::all() )
10 C++: Mat::Mat(const Mat& m, const Rect& roi)
11 C++: Mat::Mat(const CvMat* m, bool copyData=false)
12 C++: Mat::Mat(const IplImage* img, bool copyData=false)
13 C++: template explicit Mat::Mat(const Vec& vec, bool copyData=true)
14 C++: template explicit Mat::Mat(const Matx& vec, bool copyData=true)
15 C++: template explicit Mat::Mat(const vector& vec, bool copyData=false)
16 C++: Mat::Mat(int ndims, const int* sizes, int type)
17 C++: Mat::Mat(int ndims, const int* sizes, int type, const Scalar& s)
18 C++: Mat::Mat(int ndims, const int* sizes, int type, void* data, const size_t* steps=0)
19 C++: Mat::Mat(const Mat& m, const Range* ranges)
不同的构造函数对应不同的输入参数,2-5构造函数是基于二维矩阵,通过矩阵size进行定义,进行空间的开辟或初始化数据。16-18构造函数是基于多维向量的。6,9,10构造函数是通过另一个Mat实例的某个感兴趣区域对目标实例进行初始化。11,12是通过1.x类型数据信息进行初始化的构造函数。13-15构造函数通过不同的数据类型(如cv::Vec// make a 7x7 complex matrix filled with 1+3j.
Mat M(7,7,CV_32FC2,Scalar(1,3));
// and now turn M to a 100x60 15-channel 8-bit matrix.
// The old content will be deallocated
M.create(100,60,CV_8UC(15));
// create a 100x100x100 8-bit array
int sz[] = {100, 100, 100};
Mat bigCube(3, sz, CV_8U, Scalar::all(0));
2.通过成员函数进行初始化