有一种令人难以置信的技术,只需两个内存副本就可以将 Pillow 图像转换为 NumPy 数组!等等,“只需两个内存副本”是什么意思?是不是可以在只复制一次内存或根本不复制内存的情况下在库之间转换数据?
这似乎不可思议,但是更传统的图像转换方法工作速度慢1.5-2.5倍(如果你需要一个可变对象)。今天,我将深入研究这两个库并告诉你为什么会发生这种情况。此外,我将向你展示一种获得相同结果但速度更快的方法。不会有任何存储库或包,只有事实和最后的工作代码。
第一件事:关键概念
Pillow是一个 Python 图像库。它支持不同的格式,提供延迟加载,并允许从文件访问元数据。长话短说,它可以完成你加载/保存图像所需的一切。
https://pillow.readthedocs.io/en/stable/
NumPy是一个 Python 库,用于处理多维数组。它是一系列科学、计算机视觉和机器学习库(如 SciPy、Pandas、Astropy 等)的基础库。
https://numpy.org/
OpenCV是最流行的计算机视觉库,具有广泛的功能。它没有自己的图像内部存储格式,而是使用 NumPy 数组。使用这个库的常见场景是当你需要将图像从 Pillow 转换为 NumPy 以便你可以使用 OpenCV 使用它时。
https://opencv.org/
今天我将在 64 位操作系统下的 Raspberry Pi 4 1800 MHz 上运行基准测试。毕竟,如果不是在 Raspberry 上,你还有什么地方需要计算机视觉?
NumPy 转换的工作原理
以下是将 Pillow 图像转换为 NumPy 的两种最常见方法。如果你谷歌一下,你可能会找到其中之一:
numpy.array(im) — 从图像复制到 NumPy 数组。
numpy.asarray(im) — 与numpy.array(im, copy=False) 相同。据说,它不会复制,而是使用原始对象的内存。但比那复杂一点。
有人会认为在第二种情况下,NumPy 数组变成了原始图像的一种表示,如果更改 NumPy 数组,图像也会发生变化。事实上,事实并非如此:
In [1]: from PIL import Image
In [2]: import numpy
In [3]: im = Image.open('./canyon.jpg').resize((4096, 4096))
In [4]: n = numpy.asarray(im)
In [5]: n[:, :, 0] = 255
ValueError: assignment destination is read-only
In [6]: n.flags
Out[6]:
C_CONTIGUOUS : True
F_CONTIGUOUS : False
OWNDATA : False
WRITEABLE : False
ALIGNED : True
WRITEBACKIFCOPY : False
UPDATEIFCOPY : False
如果你使用numpy.array()函数,这与你得到的结果大不相同:
In [7]: n = numpy.array(im)
In [8]: n[:, :, 0] &