1 Mat 类的内存管理

使用 Mat 类,内存管理变得简单,不再像使用 IplImage 那样需要自己申请和释放内存。虽然不了解 Mat 的内存管理机制,也无碍于 Mat 类的使用,但是如果清楚了解 Mat 的内存管理,会更清楚一些函数到底操作了哪些数据。

Mat 是一个类,由两个数据部分组成:矩阵头(包含矩阵尺寸,存储方法,存储地址等信息)和一个指向存储所有像素值的矩阵的指针,如下图所示。矩阵头的尺寸是常数值,但矩阵本身的尺寸会依图像的不同而不同,通常比矩阵头的尺寸大数个数量级。复制矩阵数据往往花费较多时间,因此除非有必要,不要复制大的矩阵。

为了解决矩阵数据的传递,OpenCV 使用了引用计数机制。其思路是让每个Mat 对象有自己的矩阵头信息,但多个 Mat 对象可以共享同一个矩阵数据。让矩阵指针指向同一地址而实现这一目的。很多函数以及很多操作(如函数参数传值)只复制矩阵头信息,而不复制矩阵数据。

前面提到过,有很多中方法创建 Mat 类。如果 Mat 类自己申请数据空间,那么该类会多申请 4 个字节,多出的 4 个字节存储数据被引用的次数。引用次数存储于数据空间的后面,refcount 指向这个位置,如下图所示。当计数等于 0 时,则释放该空间。

opencv mat空间释放 opencv mat 内存_opencv


关于多个矩阵对象共享同一矩阵数据,我们可以看这个例子:

Mat A(100,100, CV_8UC1);
Mat B = A;
Mat C = A(Rect(50,50,30,30));

上面代码中有三个 Mat 对象,分别是 A,B 和 C。这三者共有同一矩阵数据,其示意图如下图所示。

opencv mat空间释放 opencv mat 内存_数据_02


图 3个矩阵共用同一个矩阵数据

2 输出

从前面的文章中,可以看到 Mat 类重载了<<操作符,可以方便地使用流操作来输出矩阵的内容。默认情况下输出的格式是类似 Matlab 中矩阵的输出格式。除了默认格式,Mat 也支持其他的输出格式。代码如下:

首先创建一个矩阵,并用随机数填充。填充的范围由 randu()函数的第二个参数和第三个参数确定,下面代码是介于 0 到 255 之间。

Mat R = Mat(3, 2, CV_8UC3);
randu(R, Scalar::all(0), Scalar::all(255));

默认格式输出的代码如下:

cout << "R (default) = " << endl << R << endl << endl;

输出结果如图所示。

opencv mat空间释放 opencv mat 内存_输出方式_03


Python 格式输出的代码如下:

cout << "R (python) = " << endl << format(R,"python") << endl << endl;

opencv mat空间释放 opencv mat 内存_opencv mat空间释放_04


以逗号分割的输出的代码如下:

cout << "R (csv) = " << endl << format(R,"csv") << endl << endl;

opencv mat空间释放 opencv mat 内存_数据_05


numpy 格式输出的代码如下:

cout << "R (numpy) = " << endl << format(R,"numpy" ) << endl << endl;

opencv mat空间释放 opencv mat 内存_内存管理_06


C 语言格式输出的代码如下:

cout << "R (c) = " <<endl << format(R,"C") << endl << endl;

opencv mat空间释放 opencv mat 内存_输出方式_07


除了 Mat 对象可以使用<<符号输出,其他的很多类型也支持<<输出。

二维点:

Point2f P(5, 1);
cout << "Point (2D) = " << P << endl << endl;

三维点:

Point3f P3f(2, 6, 7);
cout << "Point (3D) = " << P3f << endl << endl;