Mat - 基本图像容器
目标
我们有多种方法从现实世界中获取数字图像:数码相机、扫描仪、计算机断层扫描和磁共振成像等等。在任何情况下,我们(人类)看到的都是图像。然而,当我们将其转换为数字设备时,我们记录的是图像中每个点的数值。
例如,在上图中,你可以看到汽车的镜面只是一个包含所有像素点的强度值的矩阵。如何获取和存储像素值可能会根据需要而有所不同,但最终计算机世界中的所有图像都可能被简化为数值矩阵和描述矩阵本身的其他信息。OpenCV 是一个计算机视觉库,其主要重点是处理和操作这些信息。因此,我们首先需要熟悉事就是 OpenCV 如何存储和处理图像。
Mat
OpenCV 成立于 2001 年。当时,库是围绕C接口构建的,为了将图像存储在内存中,他们使用了一种名为 IplImage 的C结构。你会在大多数老的教程和教育材料中看到这些。这样做的问题是,它把C语言的所有缺点都暴露出来了。最大的问题是手动管理内存。它建立在用户负责内存分配和回收的假设之上。虽然这对于较小的程序来说不是问题,但一旦代码库增长,处理这些问题就会非常困难,无法专注于解决开发目标。
幸运的是,C++ 出现了,并引入了类的概念,通过类让用户更容易使用自动内存管理(或多或少)。好消息是,C++ 与C是完全兼容的,所以这种更改不会产生任何兼容性问题。因此,OpenCV 2.0 引入了一个新的 C++ 接口,它提供了一种新的处理方式,这意味着你不需要费心管理内存,使你的代码简洁(更少的编写,实现更多)。C++ 接口的主要缺点是,目前许多嵌入式开发系统只支持C。因此,除非你的目标是嵌入式平台,否则使用旧的方法是没有意义的(除非你是一个喜欢受虐的程序员,想自找麻烦)。
关于 Mat 需要了解的第一件事是,不再需要手动分配它的内存,并在不需要时立即释放它。大多数 OpenCV 函数将自动分配其输出数据。如果你传递一个已经存在的 Mat 对象,它已经为矩阵分配了所需的空间,这个对象将被重用。换句话说,我们在任何时候都只使用执行任务所需的内存。
Mat 基本上是一个带有两部分数据的类:矩阵头(包含矩阵大小,存储方法,矩阵存储地址等信息)和一个指向包含像素值的矩阵(采取任何维数取决于选择的存储方法)的指针。矩阵头的大小是不变的,但是矩阵本身的大小可能因图像而异,通常要大几个数量级。
OpenCV 是一个图像处理库。它包含了大量的图像处理函数集合。为了解决计算上的难题,大多数情况下将使用库中的多个函数。因此,将图像传递给函数是一种常见的做法。我们不应该忘记正在谈论的是图像处理算法,这往往是相当繁重的计算。我们最不想做的就是通过对可能很大的图像进行不必要的复制来进一步降低程序的速度。
为了解决这个问题,OpenCV 使用了一个引用计数系统。其思想是,每个 Mat 对象都有自己的头,但是,通过使矩阵指针指向同一地址,两个 Mat 对象之间可以共享一个矩阵。此外,复制操作符将只复制头和指向大型矩阵的指针,而不复制数据本身。
图像就是一个矩阵,在 OpenCV for Python 中,图像就是 NumPy 中的数组!
import sys
import cv2
def showImage(img):
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
img = cv2.imread("64.jpg", cv2.IMREAD_COLOR)
print(img)
print(img.shape[0], img.shape[1])
showImage(img)
img2 = img
img2[0:64, 0:32] = 0
print(img)
showImage(img)
showImage(img2)
储存方法
这是关于如何存储像素值的。可以选择使用的颜色空间和数据类型。颜色空间指的是我们如何组合颜色组件,以便对给定的颜色进行编码。最简单的一个是灰度,我们可以使用的颜色是黑色和白色。这些元素的组合可以让我们创造出许多灰色阴影。
对于组成颜色的方式,我们有更多的方法可供选择。它们中的每一个都可以分解成三到四个基本组件,我们可以将这些组件组合在一起来创建其他组件。最流行的是 RGB,主要是因为这也是我们的眼睛构建颜色的方式。它的底色是红、绿、蓝。为了编码颜色的透明度,有时会添加第四个元素:alpha (A)。
然而,还有许多其他的颜色系统,每一种都有自己的优势:
● RGB 是最常见的,因为我们的眼睛使用类似的方式,但是请记住,OpenCV 标准显示系统使用 BGR 颜色空间组成颜色(红色和蓝色通道交换位置)。
● HSV 和 HLS 将颜色分解为色调、饱和度和值/亮度组件,这是我们描述颜色的更自然的方式。例如,可以消除最后一个组件,使算法对输入图像的光线条件不那么敏感。
● YCrCb 被流行的 JPEG 图像格式所使用。
● CIE L*a*b* 是一个感知上均匀的颜色空间,如果你需要测量一个给定颜色到另一个颜色的差异,这个空间非常有用。
每个构建组件都有自己的有效范围。这就导致了所使用的数据类型。如何存储组件定义了我们对其范围的控制。最小的数据类型是 char,这意味着一个字节或8位。它可以是无符号的(可以存储0到255的值),也可以是有符号的(值从-127到+127)。在三个组件(如RGB)的情况下,这个宽度已经提供了 1600 万种可能的表示颜色,还可也通过为每个组件使用 float(4字节= 32位) 或 double(8字节= 64位) 数据类型来获得更精细的控制。不过,请记住,增加组件的大小也会增加内存中整个图像的大小。
官方文档:
https://docs.opencv.org/4.x/d6/d6d/tutorial_mat_the_basic_image_container.html