Python 图像读写谁最快?不信就比一比

Python 图像读写谁最快?不信就比一比

图像处理,顾名思义,是对图像进行的各种转换、计算等处理。图像处理必不可少地需要读写图像文件。

图像文件的读取,就是将图像数据从磁盘中的文件内读入内存,之后按照图像解码标准解码,最后把图像各像素的值存储到内存区域中,并返回存储图像像素值的内存起始地址。图像数据的结构,其实就是二维数组。在 C 语言里面,就是用二维数组或者一维数组形式的手动二维数组的形式存放的。在 Python 生态里,读入内存的图像,一般需要以 Numpy 数组的形式存放,方便稍后处理。Numpy 数组具有 C 接口,在特定情况下,可以直接直接用 C 读取。

图像文件的写入,是将图像数据按照图像编码标准编码,添加上如图像像素数、颜色数等相关的文件信息,构造成符合标准的图像文件数据,并写入磁盘。

用什么方法读写图像,速度最快,一直为人所争议,但不同的平台 windows/linux 32bit/64bit 与实现 python2/python3/c,不同的图像格式大小批量,得到的结果总是千奇百怪的。

所以,本质上,这个速度比较,是依赖于运行环境和具体的应用场合的。运行环境包括硬件和软件,硬件上的CPU、内存、硬盘、系统总线、缓存等等;软件包括系统平台、编译器、第三方库等等。如此多的影响变量,已经完全没办法控制了。还要加上具体的应用场合,比如,读取图像的图像格式,分辨率大小等等。典型的,JPG 图像和 BMP 图像编解码运算压力就很不一样;海量小图像,和少量大分辨率图像,差距也很大。不同的 Python 包在不同的第三方库的支持下,针对不同图像格式、文件尺寸的读写性能也是完全不同的。在后面详细读源码的时候可以发现,同一个 Python 包读写图像代码,在不同的环境下,实际调用路径和调用的第三方包也会变化。

还有一个问题,就是运行环境的具体要求。比较典型的一个问题,就是 Pillow 这种纯 Python 代码包,和 OpenCV 这种以 C/C++ 为基础的 Python 接口转接的包的区别。在跨平台的时候,纯 Python 包更能取得一致效果。我个人之前的工作中,就遇到过,使用 OpenCV 在 windows 和 Linux 环境下得到不同的运行结果,其中原因有可能是计算中 Windows 和 Linux 下调用的运算库的版本不同或具体指令得到的结果不一样。另一种常见的情况,是在云平台上,不具有管理员权限,不能额外安装二进制代码包,甚至 Python 包,那就只能尽可能利用现存工具。

因此,为了在“不论如何的复杂环境”下,都能“根据现实条件”找到“适合当前应用需求”的最高效 Python 包,我这里提出一个测试驱动的方案。毕竟只要实际上手测试一下,自然就可以找到实际最高效的 Python 包。而且,利用 Python 的“引入并改名 import as”机制,把经过测试后选择出来的函数接出来以供使用,可以在各种环境下,都提供出一个最快的解决方案。

本文的以下部分,列举了常见的具有 Python 图像读写能力的包和使用方法,分析比较其性能,并开源一个自动测试比较不同图像处理包图像读取性能的包。希望能够有助于同学、同行节省争议时间。毕竟只有具有真正实验数据支撑的“实证研究 Empirical Research”才是真正有价值的。

MATLAB 图像文件读写

在介绍 Python 的图像文件读写,先对比一下 MATLAB。

MATLAB 有 imread 和 imwrite 分别用于图像读取和图像写入。

A = imread(filename, fmt)
imwrite(A,filename)

filename 是需要读入的图像文件名,fmt是读入图像的格式代码。整句代码的意思是将文件 filename 以 fmt 格式读入,读入后,数据存储在变量A中。

Python 图像文件读写

在 Python 生态里,我个人认为,最为正统的图像读写,应该使用 skimage 包。skimage 的全称是 scikit-image。但实际使用的时候,例如在导入一个 Python 包的时候,不能存在连字符,所以有缩略语 skimage。skimage 是原本 SciPy(Python 科学计算包)中图像处理相关算法独立出来的 Python 包。skimage 包的维护一直有 Anaconda 的参与,所以使用 conda install 也是很稳妥的。

skimage 中,负责图像读写的,是 io module 中的 imread 和 imsave 函数。

from skimage import io
img_array = io.imread(fname, as_grey=False, plugin=None, flatten=None, **plugin_args)
io.imsave(fname, arr, plugin=None, **plugin_args)

MATLAB 与 Python 读取图像的 RGB 顺序,是不一样的,要注意调整,也可以具体指定 RGB 顺序,以便确保准确。

常见图像处理工具简介

最常见的图像读取工具,是 Pillow 和 OpenCV。

Pillow 是 Python 原生图像处理包,优点是纯 Python、跨平台、没有其它依赖,可靠稳定。

OpenCV 是以 C\C++ 为主开发的图像处理通用软件库,算法全面,功能强大,具有多种接口。早期版本长期锚定 Python 2,环境配置比较繁琐,但现在其 Python 3 接口已经比较完善了。


其他常见的图像处理包,包括 matplotlib、imageio 和 skimage。

matplotlib 作为 Python 最早也是影响最广泛的数据可视化包,也是常用的图像读取工具。

imageio 其实是 scipy 中的图像操作功能独立后的包,原本是 scipy.io 中的图像操作工具。

skimage 是一套完整的图像处理包,自然也具有图像读取能力。


另外几个比较独特的:

Qt 作为历史悠久的跨平台 GUI 库,图像读取方式独树一帜,效率非常好,但格式独特。

再就是 imread 包,专职图像读取,格式支持广泛,性能也不俗。

FreeImage 是一个开源图像格式支持库。


其中 imageio、skimage 其实用的就是 Pillow,只是自己在上面包装了一下。

matplotlib 除了 PNG 格式外,也都是利用一种插件机制,调用其它包实现的。依赖的,主要也还是 Pillow。而 PNG 格式,matplotlib 本身有基于 C 的支持。

具体的不同包之间的调用方法可以参见代码和我之前的回答:


图像读取性能测试 imreadeval

Github repo 如下:

包的名字,起得很直观,就是评估图像读取性能 im(age)-read-eval(uation)。

当前,仅仅实现了

  • Pillow
  • OpenCV
  • Matplotlib
  • imageio (descendent of scipy.io.imread, which is deprecated)
  • SciKit-Image (depend on SciPy)

五种图像读取函数的测试。

可以直接 pip install imreadeval 安装。

而评估测试也很简单,直接 import imreadeval 就可以。

默认会直接读取包中自带的两张(PNG、JPG 各一张)十次,并给出读取需要的时间。

这里的“读取”,是读入一张图像到内存,并读取其中一个像素的值。以防止打开图像不实际读取(Pillow,说的就是你这个 lazy 包)。


这个包针对 Py27/Py36/Py37 Win/Lin 都做了测试,都可以跑起来。方便在各种平台上做测试。

而且测试后,import 进来读取速度最快的包实现的 imread,方便在不同平台确保使用最快的 imread。

也可以直接 from imreadeval import imread引入最快的函数。


也提供了接口,可以在自己的图像上,测试哪个包读得最快。方法如下

from imreadeval import imread_eval

optimum_package_name = 
    imread_eval(filenames = ['filename1.jpg', 'filename2.png'],
                times = 1000)

可以自行 glob 一些图像,读入测试千百遍啊千百遍。



希望大家在自己的平台测试,并提供意见。欢迎 issue 和 PR,也欢迎 star 和 fork。

编辑于 2020-10-15 20:55

文章被以下专栏收录