python imread函数_Python的各种imread函数在实现方式和读取速度上有何区别?

一看到题主的问题,其实就有个猜想,其实底层应该都是一样的,或者说没几个独立的实现吧?主要是关注开源软件一段时间之后,就会发现开源界其实挺缺人的,现有这些人也都挺懒的。挖坑的人多,填坑的人少。说是这活谁都可以参与,但其实真没多少人实际做。一个活有一个人干了,其他人一般就懒得再重复了。以下就是从源码来看这些开源库的具体实现。

1. PIL.Image.open

代码在这里:Pillow/Image.py at 3.1.x · python-pillow/Pillow · GitHub​github.com

open() 函数打开图像,但并不读入,直到有操作发生。

具体的读取操作是在 ImageFile.py 写的。大体流程是先检测文件类型,整块地读入文件内容,然后调用解码器解码,做了很多优化,效率应该还是很高的。

2. scipy.misc.imread

misc 的 __init__.py 在这里:scipy/__init__.py at v0.17.1 · scipy/scipy · GitHub​github.com

调用的还是 pilutil 中的 imread

相关代码如下:

try:

from .pilutil import *

from . import pilutil

__all__ += pilutil.__all__

del pilutil

except ImportError:

pass

也算是学了一招,从 pilutil 导入其所有函数添加到当前空间,然后又删除了 pilutil 消除影响。

3. scipy.ndimage.imread

代码在这里:scipy/io.py at v0.17.1 · scipy/scipy · GitHub​github.com

imread 调用 scipy.misc.pilutil.imread。从名字就能看出来其实调用的还是 Pillow。

根据 pilutil 代码:scipy/pilutil.py at v0.17.1 · scipy/scipy · GitHub​github.com

确实是调用 pil.image.open(),然后返回一个 fromimage()。

20180410 更新:scipy.ndimage.imread,其实调用的就是 scipy.misc.imread。我之前没写清楚。代码在这里:https://github.com/scipy/scipy/blob/80e76d43f0ada0a1964195cb6d13ae519feb07e8/scipy/ndimage/io.py#L8​github.com

_have_pil = True

try:

from scipy.misc.pilutil import imread as _imread

except ImportError:

_have_pil = False

挺过分的,本身已经用的是 pillow 的 imread 了。又在自己的包里面提供了两个不同的接口。不知道的,还以为做了多少工作呢。其实同一个代码文件里面也标记了,这个 imread 要废弃了,还请直接调用 matplotlib……啊???什么鬼!!!你自己包里面用了两处 pillow 的 imread,然后废弃一个,却推荐别人用 matplotlib 替代,口是心非傲娇代码包。戏精代码如下:

# Use the implementation of `imread` in `scipy.misc.pilutil.imread`.

# If it weren't for the different names of the first arguments of

# ndimage.io.imread and misc.pilutil.imread, we could simplify this file

# by writing

# from scipy.misc.pilutil import imread

# Unfortunately, because the argument names are different, that

# introduces a backwards incompatibility.

@np.deprecate(message="`imread` is deprecated in SciPy 1.0.0.\n"

"Use ``matplotlib.pyplot.imread`` instead.")

def imread(fname, flatten=False, mode=None):

if _have_pil:

return _imread(fname, flatten, mode)

raise ImportError("Could not import the Python Imaging Library (PIL)"

" required to load image files. Please refer to"

" http://pillow.readthedocs.org/en/latest/installation.html"

" for installation instructions.")

4. skimage.io.imread

代码在这里:scikit-image/_io.py at master · scikit-image/scikit-image · GitHub​github.com

(20180410 更新:该部分代码已修改,链接不可用了

是通过插件 plugin 来读入不同的文件,而且会试用几个不同的 plugins 来找到合适的。

使用 call_plugin 来调用,代码在这里:scikit-image/manage_plugins.py at master · scikit-image/scikit-image · GitHub​github.com

可以根据如下代码查看插件调用的优先级

# For each plugin type, default to the first available plugin as defined by

# the following preferences.

preferred_plugins = {

# Default plugins for all types (overridden by specific types below).

'all': ['pil', 'matplotlib', 'qt', 'freeimage'],

'imshow': ['matplotlib'],

'imshow_collection': ['matplotlib']

}

plugins 的源代码在这里:scikit-image/scikit-image​github.com

可以看到 pil 的 imread,是用 open 打开图像之后,再转换成 ndarray。

20180410 更新:skimage 的插件管理,代码改在了这里:https://github.com/scikit-image/scikit-image/blob/37ed59d2880fa96ff908608d7f723b98cd319c9b/skimage/io/manage_plugins.py#L46​github.com

最近一次更新去掉了 freeimage 的插件选项。图像读取的基本原理还是没变。还是优先调用 Pillow,就是插件里面的 pil。

preferred_plugins = {

# Default plugins for all types (overridden by specific types below).

'all': ['pil', 'matplotlib', 'qt'],

'imshow': ['matplotlib'],

'imshow_collection': ['matplotlib']

}

5. cv2.imread

这里是调用的 CV::imread(),代码在这里:opencv/loadsave.cpp at master · opencv/opencv · GitHub​github.com

。一般来说 C\C++ 的实现,应该比 python 速度快一点。

20180410 更新:看了一下 opencv 的 python3 接口。imread 读进来的 Mat,在转换为 PyObject(Python 对象)的时候,会检查一下,是不是无符号的 UMat。如果是,直接拷贝一个指针给 PyObject;如果不是,会新开一个 Mat,读数据,再生成 PyObject。

而 CV::imread() 本身,调用的,是内部的 imread_() 函数:https://github.com/opencv/opencv/blob/6d83a8038525cf64b0edd866ebbd94906eb5691d/modules/imgcodecs/src/loadsave.cpp#L633​github.com

cvLoadImage 到 IplImage 和 cvLoadImageM 到 CvMat,其实也都是调用的 imread_(),详情见代码:https://github.com/opencv/opencv/blob/6d83a8038525cf64b0edd866ebbd94906eb5691d/modules/imgcodecs/src/loadsave.cpp#L966​github.com

就是这段:

CV_IMPL IplImage*

cvLoadImage( const char* filename, int iscolor )

{

return (IplImage*)cv::imread_(filename, iscolor, cv::LOAD_IMAGE );

}

CV_IMPL CvMat*

cvLoadImageM( const char* filename, int iscolor )

{

return (CvMat*)cv::imread_( filename, iscolor, cv::LOAD_CVMAT );

}

这里 imread_() 是图像读取的总接口,为表面的各种函数提供核心功能。图像读取的真正魔法,其实发生在 ImageDecoder 里面,代码则在 imgcodecs 目录:opencv/opencv​github.com

6. matplotlib.image.imread

matplotlib 的文档里面说,matplotlib 原生只可以读取 PNG 文件,有 PIL 的时候,可以读取其他类型的文件。如果使用 URL 打开在线图像文件,需要符合 PIL 的文档要求。

matplotlib.image.imread 的代码在这里:matplotlib/image.py at master · matplotlib/matplotlib · GitHub​github.com

matplotlib 的原生 PNG 读取和写入,是用 C 实现的,代码在这里:matplotlib/_png.cpp at master · matplotlib/matplotlib · GitHub​github.com

matplotlib 是先用 pil 的 open 打开图像,如果格式是 png,就用原生方法打开。相关代码如下:

handlers = {'png': _png.read_png, }

if format is None:

if cbook.is_string_like(fname):

parsed = urlparse(fname)

# If the string is a URL, assume png

if len(parsed.scheme) > 1:

ext = 'png'

else:

basename, ext = os.path.splitext(fname)

ext = ext.lower()[1:]

elif hasattr(fname, 'name'):

basename, ext = os.path.splitext(fname.name)

ext = ext.lower()[1:]

else:

ext = 'png'

else:

ext = format

if ext not in handlers:

im = pilread(fname)

if im is None:

raise ValueError('Only know how to handle extensions:%s; '

'with Pillow installed matplotlib can handle '

'more images' % list(six.iterkeys(handlers)))

return im

声明的处理器只有 png。如果是 png 文件,调用 _png.read_png。如果不是 png 直接使用 pilread(就是用 pil 的 Image.open 然后 pil_to_array)。

matplotlib 的源码确实比较复杂,一大部分主体是用 C 写的,改动很激进,功能更新猛烈。

20180410 更新:matplotlib.image.imread() 是原生的。改名版有 matplotlib.pyplot.imread(),车祸现场见:https://github.com/matplotlib/matplotlib/blob/dec87a31379c617704fce7225bfd1ad0dd799141/lib/matplotlib/pyplot.py#L2209​github.com

from matplotlib.image import imread as _imread

...

@docstring.copy_dedent(_imread)

def imread(fname, format=None):

return _imread(fname, format)

改名版还有 matplotlib.pylab.imread,代码见:https://github.com/matplotlib/matplotlib/blob/438aa08df1037f059b4915b9a28871a90ae89976/lib/matplotlib/pylab.py#L246​github.com

from matplotlib.pyplot import *

小结

从源码看,绝大多数常用代码库其实都还是用的 PIL。原因容易理解,PIL 是纯 Python 实现的,而且经过了优化,性能应该还不错。除了 PIL 之外,常用的就是 OpenCV 和 Qt。OpenCV 和 Qt 是 C/C++ 实现的,可能速度会快一些,但具体要测试才知道。稍后会在 GitHub 上开一个对比测试的 Repo 再测试一下性能。

scikit-images 的插件机制很灵活有趣,尤其是它提供了其他几种图像读取的实现。

matplotlib 代码稍显乱一点,但更证明了其功能迭代速度快,开发激进。用 matplotlib 尽量多用PNG 以便利用原生的 PNG 支持

补充

补充一些读代码时遇到的一些其他的图像读取方法。

7. PyQt4.QtGui.QImage.load(filename)

8. freeimage

scikit-image 是使用 ctype 调用 freeimage 的动态链接库,来调用 Freeimage_Load()。代码在此:freeimage/FreeImage.h at master · imazen/freeimage · GitHub​github.com

9. imread

其在 PyPi 上的主页:Python Package Index​pypi.python.org

代码在此:imread/imread.py at master · luispedro/imread · GitHub​github.com

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值