自从OpenCV产生以来,其函数库一直是基于C接口构建的,因此在最初的几个OpenCV版本中,一般使用名为IplImage的C语言结构体在内存中存储图像。直到现在,仍然出现在大多数的旧版教程中。
对于OpenCV1.x时代的基于C语言接口而建的图像存储格式IplImage*,如果在退出前忘记release掉的话,就会造成内存泄漏,而且用起来也很不方便,我们在调试的时候,往往也会花费很多时间在手动释放内存的问题上面。虽然对于小型的程序来说,手动管理内存不是问题,但如果代码量很大的话,维护的成本就会越来越大,我们便会开始越来越多的纠缠于内存管理的问题,而不是着力解决最终的开发目标。
C++带来了类的概念,这使我们有了另外一个选择:自动的内存管理,在OpenCV2.0版本中引入了一个新的C++接口,利用自动内存管理给出了解决问题的新方法。使用此方法,我们不需要再纠结在管理内存的问题,而且代码会变得干净整洁。
关于Mat类,我们首先应该知道的是:
- 不必再手动为其开辟空间。
- 不必再在不需要时立即将空间释放。
当传递一个已经存在的Mat对象时,开辟好的矩阵空间会被重用。也就是说,我们每次都使用大小正好的内存来完成任务。
总而言之,Mat是一个类,由两个数据部分组成:矩阵头(包含矩阵尺寸、存储方法、存储地址等信息)和一个指向存储所有像素值的矩阵(根据所选存储方法的不同,矩阵可以是不同的维数)的指针。矩阵头的尺寸是常数值,但矩阵本身的尺寸会依据图像的不同而不同,通常比矩阵头的尺寸大几个数量级。因此当在程序中传递图像并创建副本时,大的开销是由矩阵造成的。而不是信息头。所以当我们在编写图像处理算法时,尽量减少大图像的复制,因为这会降低程序的运行速度。
为了解决这个问题,OpenCV引用计数机制。让每个Mat对象有自己的信息头,但共享一个矩阵。通过让矩阵指针指向同一地址而实现。而拷贝构造函数则只复制信息头和矩阵指针,而不复制矩阵。
例如:
Mat src,dst;//只创建信息头部分
src = imread("1.jpg"); //这里为矩阵开辟内存
Mat dst(src); //使用拷贝构造函数
dst = src; //赋值运算符
以上代码中所有的Mat对象都指向同一个也是唯一一个数据矩阵。虽然他们的信息头不同,但通过任何一个对象所做的改变也会影响其他对象。实际上,不同对象只是访问相同数据的不同途径。
如果矩阵属于多个Mat对象,那么当不再使用它时,最后一个使用的对象负责清理。但某些时候你还想复制矩阵本身(不只是矩阵头和矩阵指针,这时可以使用clone()和copyTo()。
总结:
- OpenCV中输出图像的内存分配是自动完成的。
- 使用OpenCV的C++接口时不需要考虑内存释放的问题。
- 赋值运算符和拷贝构造函数只复制信息头。
- 使用clone()和copyTo()来复制一副图像的矩阵。