在了解基本图像容器之前,我们需要了解数字照点在计算机中的存储格式。我们有很多方法来获取现实世界中的图像,这些图像被数字化后才能存储到计算机。图像的存储格式十分严格,每一张图片,实际上就是一个二进制文件。我们可以用支持十六进制(二进制)的文本编辑器,打开一个图片文件。
(用UltraEdit打开的图片文件)
由上图可知,计算机中的图片,实际是二进制文件。我们平时打开一张图片,看到的图像,是看图软件根据二进制的值在计算机屏幕上,一个一个地将像素点打印出来的结果。理论上,我们可以直接修改二进制文件的值,来达到处理图片的目的。然而图像的文件存储格式,实在太严格了。所以,一般进行图像处理的时候,都是基于开源库OpenCV.
想要知道OpenCV是如何处理和操纵图片信息之前,我们不得不提及OpenCV内置的基本图像容器。所谓容器,就是能把图片在内存中的信息结构化,使图片的信息能被操作。
OpenCV的老版本是用C语言写的,之前的图像容器是IplImage,并且 是用结构体来定义的。很显然,C语言不支持自动内存管理,所以这种方式对于新手来说,可能会带来巨大的缺陷(处理一个大视频文件,没有及时释放内存)。在后续的版本中,OpenCV采用C++进行重构了,并把基本图像容器改为Mat。这样的好处就是,我们不用手动地管理内存了。虽然C++包括标准模板库STL都没有真正意义上的自动内存回收机制,但我们不用担心这么多,因为OpenCV恰到好处地在合适的地方进行了内存释放。
关于Mat
我们在使用OpenCV的时候,只需要知道Mat是一个类就行了。Mat由两部分构成:矩阵头(matrixheader)、指向矩阵的指针(pointer to the matrix)。
关于Mat我们还需要知道一点。有的时候要处理的图像文件特别大,如果我们定义了一个Mat的对象A和B,当我们把图像文件加载到A后,我们又想通过B来访问文件。这个时候,如果直接复制,肯定会造成很大的空间浪费。OpenCV在这一点上考虑得十分周到,它使用一种被称作引用计数系统(reference counting system)的机制来解决这种潜在的缺陷。
这种机制的核心思想,是让每一个Mat对象拥有一个header和一个指向图像数据的指针。当把A复制给B的时候,B拥有的仅仅是A的指针,而不存储图像数据的内容。
上面所有的对象A,B,C,最后都指向同一个图像数据文件,然而它们的文件头却不一样。修改其中的任何一个的信息,都会影响到其他对象。
实际上我们可以这样理解,OpenCV为每一个Mat对象提供了不同的访问潜在图像数据的方法,但是这些不同的Mat对象的header是不同的。我们甚至可以创建一个header,使得引用的数据仅仅是图像的一部分。例如高速公路上,抓拍到的照片,是一个图像文件,我们定义两个Mat对象,A和B.A引用整张图片,而B引用图中的车牌位置。这样的操作在OpenCV里很好实现。OpenCV内置了两种这样创建分部(subsection)的方法:通过定义矩形,通过指定行列。
用Mat存储图像
下面,我们在OpenCV中读入一张图片,并用Mat来存储,然后把存储的矩阵写到文本文件中。
接着,我们打开rst.txt,可以看到如下结果