Python的各種imread函數在實現方式和讀取速度上有何區別?
1. PIL.Image.open
2. scipy.ndimage.imread3. scipy.misc.imread4. skimage.io.imread5. cv2.imread6. 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的計算難點在哪裡?