Python的各種imread函數在實現方式和讀取速度上有何區別?

1. PIL.Image.open

2. scipy.ndimage.imread

3. scipy.misc.imread

4. skimage.io.imread

5. cv2.imread

6. matplotlib.image.imread

7. 其他的請補充...

在何種情況下推薦使用哪一個呢?


一看到題主的問題,其實就有個猜想,其實底層應該都是一樣的,或者說沒幾個獨立的實現吧?主要是關注開源軟體一段時間之後,就會發現開源界其實挺缺人的,現有這些人也都挺懶的。挖坑的人多,填坑的人少。說是這活誰都可以參與,但其實真沒多少人實際做。一個活有一個人幹了,其他人一般就懶得再重複了。以下就是從源碼來看這些開源庫的具體實現。

1. PIL.Image.open

代碼在這裡:Pillow/Image.py at 3.1.x · python-pillow/Pillow · GitHub

open() 函數打開圖像,但並不讀入,直到有操作發生。

具體的讀取操作是在 ImageFile.py 寫的。大體流程是先檢測文件類型,整塊地讀入文件內容,然後調用解碼器解碼,做了很多優化,效率應該還是很高的。

2. scipy.ndimage.imread

代碼在這裡:scipy/io.py at v0.17.1 · scipy/scipy · GitHub

imread 調用 scipy.misc.pilutil.imread。從名字就能看出來其實調用的還是 Pillow。

根據 pilutil 代碼:scipy/pilutil.py at v0.17.1 · scipy/scipy · GitHub

確實是調用 pil.image.open(),然後返回一個 fromimage()。

3. scipy.misc.imread

misc 的 __init__.py 在這裡:scipy/__init__.py at v0.17.1 · scipy/scipy · GitHub

調用的還是 pilutil 中的 imread

相關代碼如下

try:
from .pilutil import *
from . import pilutil
__all__ += pilutil.__all__
del pilutil
except ImportError:
pass

也算是學了一招,從 pilutil 導入其所有函數添加到當前空間,然後又刪除了 pilutil 消除影響。

4. skimage.io.imread

代碼在這裡:scikit-image/_io.py at master · scikit-image/scikit-image · GitHub

是通過插件 plugin 來讀入不同的文件,而且會試用幾個不同的 plugins 來找到合適的。

使用 call_plugin 來調用,代碼在這裡:scikit-image/manage_plugins.py at master · scikit-image/scikit-image · GitHub

可以根據如下代碼查看插件調用的優先順序

# 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/skimage/io/_plugins at master · scikit-image/scikit-image · GitHub。可以看到 pil 的 imread,是用 open 打開圖像之後,再轉換成 ndarray。

5. cv2.imread

這裡是調用的 CV::imread(),代碼在這裡:opencv/loadsave.cpp at master · opencv/opencv · GitHub。一般來說 CC++ 的實現,應該比 python 速度快一點。

6. matplotlib.image.imread

matplotlib 的文檔裡面說,matplotlib 原生只可以讀取 PNG 文件,有 PIL 的時候,可以讀取其他類型的文件。如果使用 URL 打開在線圖像文件,需要符合 PIL 的文檔要求。

matplotlib.image.imread 的代碼在這裡:matplotlib/image.py at master · matplotlib/matplotlib · GitHub。matplotlib 的原生 PNG 讀取和寫入,是用 C 實現的,代碼在這裡:matplotlib/_png.cpp at master · matplotlib/matplotlib · GitHub。

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 寫的,改動很激進,功能更新猛烈。

小結

從源碼看,絕大多數常用代碼庫其實都還是用的 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

9. imread

其在 PyPi 上的主頁:https://pypi.python.org/pypi/imread。代碼在此:imread/imread.py at master · luispedro/imread · GitHub。


我以前調用Caffe的python介面時做過一個測試,是拿opencv和scikit-image的比較,比較的是載入圖像和縮放圖像:


推薦閱讀:

三維碼的應用前景?
彩色圖片是通過何種演算法變成黑白圖片?
用OpenCV處理圖圖片,只顯示圖片中的黃色乒乓球,其他區域為黑色?
我目前是研一的計算機視覺學生,我的課題是醫學圖像的分割,但我在matlab opencv itk徘徊?
NUFFT的計算難點在哪裡?

TAG:Python | 圖像處理 | QtC開發框架 | OpenCV |