Xicheng 记录感兴趣的点滴

Mat - 基本的图像容器

2016-09-07

Mat - The Basic Image Container

原始地址:http://docs.opencv.org/3.0-beta/doc/tutorials/core/mat_the_basic_image_container/mat_the_basic_image_container.html

翻译前言:

自己学习 OpenCV 的时候,对很多概念不是太清楚。准备趁着翻译的的机会继续学习,同时留下一些记录。现在没有看到很多的关于 OpenCV 3 版本以上的学习资料,大部分都还非常老。

我翻译的时候没有逐字翻译,一些地方自己读了以后留下主要意思,感觉比较啰嗦的地方就掠过了。

目标

我们有非常多的方式从现实世界中获取数字图像的方法:数码相机、扫描仪、断层扫描、核磁共振等例子。所有的方法获得的都是我们(人类)能够看到的图像。无论如何,当我们要把这些图像转化到我们的数字设备上时,我们都会将其记录为图像中每一个像素点的一些数字。

A matrix of the mirror of a car

例如上面这个汽车后视镜的图像,不过就是一个矩阵,在矩阵中包含了每一个像素点的光强值。根据我们的需求,可以有很多记录这个像素值的办法,但是归根结底,所有计算机中的图像都可以缩减为数值矩阵,以及描述这个矩阵本身所包含的其他信息。 OpenCV 是这么一个计算机视觉的库,用来处理和控制这些信息。因此,你首先需要知道 OpenCV 存储和处理图像。

Mat

OpenCV 在 2001 年就已经出现。在那个时候它是用 C 语言编写的,它存储图像的方式是用 C 语言的 IplImage 结构存储在内存中。在大多数古老的指南和教材中你都能看到。因为用的 C 语言,最大的问题是需要 自己来管理内存 ,当程序还比较小的时候,这不是什么问题;一旦当程序非常大的时候,就要在内存管理里面挣扎了,难以集中去写有效的代码。

非常幸运的是 C++ 出现了,类的引入并通过 自动的内存管理 ,让用户使用变得更加容易了(或多或少的)。非常好的是 C++ 和 C 是完全兼容的,所以不会在迁移的时候发生兼容性的问题。因此 OpenCV 2.0 引入了新的 C++ 接口,它可以自动管理内存,让你的代码更加精简(写的更少,实现功能更多)。使用 C++ 主要的缺点在于,放弃了很多嵌入式系统,它们很多都只支持 C 。所以除非是你需要在嵌入式平台开发,就没有必要再用这种古老的方式(除非你是受虐狂程序员而且你就是喜欢和自己过不去)。

对于 Mat 你首先要知道的是,你再也不需要人工的申请内存空间和释放空间了。虽然这么做也是可以的,但是大部分的 OpenCV 函数都已经可以自动过的为它的输出申请空间了。当然如果是传递给函数一个已经存在的 Mat ,并且这个 Mat 已经有了矩阵的空间,那么 Mat 的空间就会被重复利用。换言之,我们最好是在执行任务的时候,只用我们需要的有限的空间最好。

Mat 是一个有两个数据部分的类: 矩阵的头部(包含了矩阵大小,存储方式,储存地址等),以及一个指针指向了包含了像素值的矩阵(由储存的方式决定了其维度)。矩阵的头部大小是固定的,但是矩阵本身的大小是根据图像发生变化的,通常它会比头部大很多数量级。

OpenCV 是一个图像处理库。它包含了非常多的图像处理函数。你会使用库中的函数来解决大多数计算量的挑战。所以对函数传递图像是一个很常见的操作。我们应当记住我们在谈论的是计算量极大的图像处理算法。我们想要做到的是,尽量不要复制那些非常大的且不必要图像,以免程序的速度降低。

为了完成这个问题, OpenCV 用了一个 引用计数 的系统。每一个 Mat 对象都有自己的头,但是矩阵数据可以被多个 Mat 共享, 即多个 Mat 的指针指向同一个矩阵地址。此外,复制操作将只会复制 Mat 的头和指向矩阵的指针,而不会复制数据本身

Mat A, C;	// 只建立了Mat头部
A = imread(argv[1], IMREAD_COLOR);	// 这里我们将会知道使用的方法(申请矩阵空间)
Mat B(A);	// 使用构造函数复制
C = A;	// 赋值运算

所有的以上对象都指向了同一片地址空间。它们的头是不一样的,但是对它们其中任何一个做改动,都会影响到其他的对象。事实上这些不同的对象只是提供针对同一个矩阵空间的了不同的访问方法。虽然如此,它们的头也是不同的。最有趣的是,你可以创造不同的头 ,指向一整块数据的一部分区域。例如,你可以创建一个兴趣区域region of interest(ROI),你只需要用新的边界来创建一个新的头部:

Mat D (A, Rect(10, 10, 100, 100) ); // 使用一个矩形区域
Mat E = A(Range::all(), Range(1,3)); // 使用行和列的边界

现在你可能要问,当多个矩阵共用一片内存空间的时候,当这个空间不再需要时,是哪一个矩阵来释放空间呢?一个简单的回答就是:最后一个使用空间的矩阵来释放内存空间。

-未完待续-


Comments