前言
OpenCV1时代采用基于C语言接口构建函数库,使用名为IplImage的结构体在内存中存储图像,其问题在于需要用户手动管理内存,如果不手动释放内存会造成内存泄漏。
OpenCV2引入面向对象编程思想,加入了一个c 接口,使用Mat类数据结构作为主打,可以实现自动内存管理,且扩展性大大提高。
Mat概述
对于Mat类,首先要知道的是
1)不必手动为其开辟空间;
2)不必再在不需要时将空间释放。
但手动做还也是可以的:大多数OpenCV函数仍会手动地为输出数据开辟空间。当传递一个已经存在的 Mat 对象时,开辟好的矩阵空间会被重用。
Mat是一个类,由两个数据部分组成:矩阵头(包含矩阵尺寸、存储方法、存储地址等)和一个指向存储所有像素值矩阵的指针。
Mat类最重要的一点是浅拷贝和深拷贝问题。由于OpenCV处理图像时很多时候没有必要重新复制一份图像矩阵,因而采用了引用计数机制。其思路是让每个Mat对象有自己的信息头,但共享一个图像矩阵(矩阵指针指向同一地址)。赋值运算符和拷贝构造函数只复制矩阵头和矩阵指针,而不复制矩阵。
Mat A , C;
A = imread(argv[1], CV_LOAD_IMAGE_COLOR); // 为矩阵开辟内存
Mat B(A); // 拷贝构造函数
C = A; // 赋值
这里A、B、C矩阵头不同,但指向了相同的图像矩阵,其中一个对象对矩阵数据进行改变也会影响其他对象。如果矩阵属于多个Mat对象,由最后一个使用它的对象进行内存清理。这通过引用计数来判断,每复制一个Mat对象,计数器加一,每释放一个计数器减一,当计数器值为0时矩阵就会被清理。
如果需要进行对象的深拷贝可以采用clone()函数或者copyTo()。
Mat A;
A = imread(argv[1], CV_LOAD_IMAGE_COLOR);
Mat B = A.clone();
Mat C;
A.copyTo(C);
Mat类的数据成员
/*
flag的详细解释可以看 https://blog.csdn.net/yiyuehuan/article/details/43701797
0-2位 depth:每一个像素的位数,也就是每个通道的位数,即数据类型(如CV_8U)
enum { CV_8U=0, CV_8S=1, CV_16U=2, CV_16S=3, CV_32S=4, CV_32F=5, CV_64F=6 }
8U 表示 8 位无符号整数,16S 表示 16 位有符号整数,64F表示 64 位浮点数
3-11位 number of channels:代表通道数channels,最高512位
0-11位共同代表type:矩阵元素的类型,即通道数和数据类型(如CV_8UC3、CV_16UC2)
14位 continuity flag:代表Mat的内存是否连续
15位 submat flag:代表该Mat是否为某一个Mat的submatrix
16-31位 the magic signature:用来区分Mat的类型,如果Mat和SparseMat
*/
int flags;
//矩阵的维数,一般大于2
int dims;
//矩阵的行数与列数,超过2维矩阵时(-1,-1)
int rows, cols;
//指向存放矩阵数据的内存
uchar* data;
//用来控制ROI区域,来获取一些图像的局部切片,减少计算量或者特殊需求的。
const uchar* datastart;
const uchar* dataend;
const uchar* datalimit;