PIL及matplotlib:OSError: cannot identify image file錯誤及解決方式
前言
這個錯誤是在使用PIL及matplotlib來讀圖(.jpg檔)的時候發生的。
以下是讀圖時所使用的指令:
from PIL import Image
Image.open('xxx.jpg')
及:
import matplotlib.pyplot as plt
plt.imread('xxx.jpg')
這個錯誤只有在讀某個特定的圖檔時才會發生。
但是後來去檢查圖片,卻發現可以用一般的圖片瀏覽器打開,並且看不出什麼問題。
錯誤訊息
使用PIL讀圖時報的錯:
OSErrorTraceback (most recent call last)
<ipython-input-4-ac0decf8a181> in <module>()
----> 1 Image.open(’./images/aireplane/21.jpg’)
/usr/local/lib/python3.5/dist-packages/PIL/Image.py in open(fp, mode)
2517 fp.close()
2518 raise IOError(“cannot identify image file %r”
-> 2519 % (filename if filename else fp))
2520
2521 #
OSError: cannot identify image file ‘./images/aireplane/21.jpg’
使用matplotlib讀圖時報的錯:
OSErrorTraceback (most recent call last)
<ipython-input-3-62fde97010d1> in <module>()
----> 1 im=plt.imread(’./images/aireplane/21.jpg’)/usr/local/lib/python3.5/dist-packages/matplotlib/pyplot.py in imread(*args, **kwargs)
2312 @docstring.copy_dedent(_imread)
2313 def imread(*args, **kwargs):
-> 2314 return _imread(*args, **kwargs)
2315
2316/usr/local/lib/python3.5/dist-packages/matplotlib/image.py in imread(fname, format)
1276
1277 if ext not in handlers:
-> 1278 im = pilread(fname)
1279 if im is None:
1280 raise ValueError('Only know how to handle extensions: %s; ’/usr/local/lib/python3.5/dist-packages/matplotlib/image.py in pilread(fname)
1254 except ImportError:
1255 return None
-> 1256 with Image.open(fname) as image:
1257 return pil_to_array(image)
1258/usr/local/lib/python3.5/dist-packages/PIL/Image.py in open(fp, mode)
2517 fp.close()
2518 raise IOError(“cannot identify image file %r”
-> 2519 % (filename if filename else fp))
2520
2521 #OSError: cannot identify image file ‘./images/aireplane/21.jpg’
問題排查
問題的解決方法是筆者不經意發現的。
因為PIL跟matplotlib都無法讀圖,所以想安裝其它的包試試看。
一開始試了opencv-python,安裝完成後,發現使用:
import cv2
cv2.imread('xxx.jpg')
可以成功讀圖。
後來試了scikit-image,安裝完成後,使用:
import skimage
skimage.io.imread('xxx.jpg')
也可以成功讀圖。
skimage安裝完成後,筆者嘗試使用PIL跟matplotlib再次讀圖,發現竟然都成功了!
後來回去看skimage安裝的log:
Collecting scikit-image
Downloading https://files.pythonhosted.org/packages/cd/9e/faa89cb088caddaffd27629248182be3f7daa52ebeb8fd1657975fdb9454/scikit_image-0.14.1-cp35-cp35m-manylinux1_x86_64.whl (25.2MB)
100% |################################| 25.2MB 67kB/s eta 0:00:011 15% |#### | 3.9MB 842kB/s eta 0:00:26
Requirement already satisfied: matplotlib>=2.0.0 in /usr/local/lib/python3.5/dist-packages (from scikit-image)
Collecting PyWavelets>=0.4.0 (from scikit-image)
Downloading https://files.pythonhosted.org/packages/24/3a/08106f608c5aceced7cd5c628f509f0a10214132a30ca99f5115121f902d/PyWavelets-1.0.1-cp35-cp35m-manylinux1_x86_64.whl (4.4MB)
100% |################################| 4.4MB 268kB/s ta 0:00:011
Collecting pillow>=4.3.0 (from scikit-image)
Downloading https://files.pythonhosted.org/packages/bc/cc/b6e47b0075ca4267855d77850af7ea4194d2fc591664f1d70e5151b50637/Pillow-5.3.0-cp35-cp35m-manylinux1_x86_64.whl (2.0MB)
100% |################################| 2.0MB 392kB/s ta 0:00:011
Requirement already satisfied: scipy>=0.17.0 in /usr/local/lib/python3.5/dist-packages (from scikit-image)
Collecting cloudpickle>=0.2.1 (from scikit-image)
Downloading https://files.pythonhosted.org/packages/fc/87/7b7ef3038b4783911e3fdecb5c566e3a817ce3e890e164fc174c088edb1e/cloudpickle-0.6.1-py2.py3-none-any.whl
Collecting dask[array]>=0.9.0 (from scikit-image)
Downloading https://files.pythonhosted.org/packages/b4/e5/6407c2349622699eab0881e6bc6e978b8da67872a105e5e20b72ff190c65/dask-1.0.0-py2.py3-none-any.whl (685kB)
100% |################################| 686kB 612kB/s ta 0:00:011 26% |######## | 184kB 576kB/s eta 0:00:01
Collecting networkx>=1.8 (from scikit-image)
Downloading https://files.pythonhosted.org/packages/f3/f4/7e20ef40b118478191cec0b58c3192f822cace858c19505c7670961b76b2/networkx-2.2.zip (1.7MB)
100% |################################| 1.7MB 391kB/s ta 0:00:011 47% |############### | 808kB 31.1MB/s eta 0:00:01
Requirement already satisfied: six>=1.10.0 in /usr/local/lib/python3.5/dist-packages (from scikit-image)
Requirement already satisfied: pytz in /usr/local/lib/python3.5/dist-packages (from matplotlib>=2.0.0->scikit-image)
Requirement already satisfied: numpy>=1.7.1 in /usr/local/lib/python3.5/dist-packages (from matplotlib>=2.0.0->scikit-image)
Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.5/dist-packages (from matplotlib>=2.0.0->scikit-image)
Requirement already satisfied: pyparsing!=2.0.0,!=2.0.4,!=2.1.2,!=2.1.6,>=1.5.6 in /usr/local/lib/python3.5/dist-packages (from matplotlib>=2.0.0->scikit-image)
Requirement already satisfied: python-dateutil in /usr/local/lib/python3.5/dist-packages (from matplotlib>=2.0.0->scikit-image)
Collecting toolz>=0.7.3; extra == “array” (from dask[array]>=0.9.0->scikit-image)
Downloading https://files.pythonhosted.org/packages/14/d0/a73c15bbeda3d2e7b381a36afb0d9cd770a9f4adc5d1532691013ba881db/toolz-0.9.0.tar.gz (45kB)
100% |################################| 51kB 618kB/s ta 0:00:011
Collecting decorator>=4.3.0 (from networkx>=1.8->scikit-image)
Downloading https://files.pythonhosted.org/packages/bc/bb/a24838832ba35baf52f32ab1a49b906b5f82fb7c76b2f6a7e35e140bac30/decorator-4.3.0-py2.py3-none-any.whl
Building wheels for collected packages: networkx, toolz
Running setup.py bdist_wheel for networkx … done
Stored in directory: /root/.cache/pip/wheels/68/f8/29/b53346a112a07d30a5a84d53f19aeadaa1a474897c0423af91
Running setup.py bdist_wheel for toolz … done
Stored in directory: /root/.cache/pip/wheels/f4/0c/f6/ce6b2d1aa459ee97cc3c0f82236302bd62d89c86c700219463
Successfully built networkx toolz
Installing collected packages: PyWavelets, pillow, cloudpickle, toolz, dask, decorator, networkx, scikit-image
Found existing installation: Pillow 4.2.1
Uninstalling Pillow-4.2.1:
Successfully uninstalled Pillow-4.2.1
Found existing installation: decorator 4.1.2
Uninstalling decorator-4.1.2:
Successfully uninstalled decorator-4.1.2
Successfully installed PyWavelets-1.0.1 cloudpickle-0.6.1 dask-1.0.0 decorator-4.3.0 networkx-2.2 pillow-5.3.0 scikit-image-0.14.1 toolz-0.9.0
You are using pip version 9.0.1, however version 18.1 is available.
You should consider upgrading via the ‘pip install --upgrade pip’ command.
發現它卸載了4.2.1版的Pillow並重新安裝了5.3.0版。
因此我們可以猜測,問題就是出在Pillow的版本上。
後來筆者做個實驗,重新使用:
pip install Pillow==4.2.0
來將Pillow還原成舊版。這時問題果然又出現了!
所以問題確定是出在Pillow的版本上!
解決方法
既然知道了問題是出在Pillow的版本上,那麼使用以下其中一個指令來升級Pillow就可以解決問題:
pip install Pillow==5.3.0
pip install --upgrade Pillow
後記
這個問題的解決辦法是升級Pillow,但是為何升級個Pillow就改變了PIL及matplotlib的行為呢?
筆者後來去查了PIL跟Pillow的關係及matplotlib讀圖的方式,將之記錄於下。
PIL跟Pillow的關係
去PIL官網查看:
Pillow is the friendly PIL fork by Alex Clark and Contributors. PIL is the Python Imaging Library by Fredrik Lundh and Contributors.
去PIL官網 - What about PIL?查看PIL的訊息:
As more time passes since the last PIL release (1.1.7 in 2009), the likelihood of a new PIL release decreases. However, we’ve yet to hear an official “PIL is dead” announcement.
總結一下:PIL自2009年以來就沒有再推出新的版本,而Pillow則是PIL的fork。
這又衍生出一個問題:既然Pillow
是用pip install Pillow
來安裝,但是為何在python裡卻是下import PIL
來調用呢?
首先在python裡查看包的路徑:
import PIL
PIL
<module ‘PIL’ from ‘/usr/local/lib/python3.5/dist-packages/PIL/init.py’>
發現目錄名稱確實是PIL。
再去Pillow的pypi頁面下載whl
檔做驗證:
wget https://files.pythonhosted.org/packages/bc/cc/b6e47b0075ca4267855d77850af7ea4194d2fc591664f1d70e5151b50637/Pillow-5.3.0-cp35-cp35m-manylinux1_x86_64.whl
接著查看whl
檔的內容,發現Pillow的whl
檔確實是有PIL的。
unzip Pillow-5.3.0-cp35-cp35m-manylinux1_x86_64.whl
Archive: Pillow-5.3.0-cp35-cp35m-manylinux1_x86_64.whl
inflating: Pillow-5.3.0.dist-info/WHEEL
inflating: Pillow-5.3.0.dist-info/top_level.txt
inflating: Pillow-5.3.0.dist-info/zip-safe
inflating: Pillow-5.3.0.dist-info/LICENSE
inflating: Pillow-5.3.0.dist-info/METADATA
inflating: Pillow-5.3.0.dist-info/RECORD
inflating: PIL/GimpGradientFile.py
inflating: PIL/PcfFontFile.py
inflating: PIL/FliImagePlugin.py
inflating: PIL/ImageCms.py
inflating: PIL/ImageFont.py
inflating: PIL/FtexImagePlugin.py
inflating: PIL/BmpImagePlugin.py
inflating: PIL/ImageFile.py
inflating: PIL/TarIO.py
inflating: PIL/PixarImagePlugin.py
inflating: PIL/XbmImagePlugin.py
inflating: PIL/IcnsImagePlugin.py
inflating: PIL/features.py
inflating: PIL/MpoImagePlugin.py
inflating: PIL/BufrStubImagePlugin.py
inflating: PIL/ExifTags.py
inflating: PIL/_tkinter_finder.py
inflating: PIL/_binary.py
inflating: PIL/DcxImagePlugin.py
inflating: PIL/McIdasImagePlugin.py
inflating: PIL/ContainerIO.py
inflating: PIL/GribStubImagePlugin.py
inflating: PIL/ImageMorph.py
inflating: PIL/GifImagePlugin.py
inflating: PIL/ImageWin.py
inflating: PIL/SunImagePlugin.py
inflating: PIL/ImtImagePlugin.py
inflating: PIL/PcxImagePlugin.py
inflating: PIL/ImagePath.py
inflating: PIL/ImageFilter.py
inflating: PIL/IptcImagePlugin.py
inflating: PIL/init.py
inflating: PIL/_imagingmorph.cpython-35m-x86_64-linux-gnu.so
inflating: PIL/Image.py
inflating: PIL/XVThumbImagePlugin.py
inflating: PIL/PdfParser.py
inflating: PIL/WmfImagePlugin.py
inflating: PIL/ImageDraw.py
inflating: PIL/MicImagePlugin.py
inflating: PIL/_imagingtk.cpython-35m-x86_64-linux-gnu.so
inflating: PIL/FitsStubImagePlugin.py
inflating: PIL/SpiderImagePlugin.py
inflating: PIL/PpmImagePlugin.py
inflating: PIL/GimpPaletteFile.py
inflating: PIL/BlpImagePlugin.py
inflating: PIL/ImageMode.py
inflating: PIL/ImageColor.py
inflating: PIL/ImageShow.py
inflating: PIL/EpsImagePlugin.py
inflating: PIL/PalmImagePlugin.py
inflating: PIL/MpegImagePlugin.py
inflating: PIL/SgiImagePlugin.py
inflating: PIL/Hdf5StubImagePlugin.py
inflating: PIL/Jpeg2KImagePlugin.py
inflating: PIL/BdfFontFile.py
inflating: PIL/WalImageFile.py
inflating: PIL/DdsImagePlugin.py
inflating: PIL/TgaImagePlugin.py
inflating: PIL/XpmImagePlugin.py
inflating: PIL/JpegPresets.py
inflating: PIL/ImagePalette.py
inflating: PIL/_version.py
inflating: PIL/_imagingmath.cpython-35m-x86_64-linux-gnu.so
inflating: PIL/ImageDraw2.py
inflating: PIL/ImImagePlugin.py
inflating: PIL/ImageTransform.py
inflating: PIL/PaletteFile.py
inflating: PIL/ImageStat.py
inflating: PIL/_util.py
inflating: PIL/MspImagePlugin.py
inflating: PIL/GdImageFile.py
inflating: PIL/ImageOps.py
inflating: PIL/ImageGrab.py
inflating: PIL/TiffTags.py
inflating: PIL/PdfImagePlugin.py
inflating: PIL/ImageChops.py
inflating: PIL/GbrImagePlugin.py
inflating: PIL/ImageEnhance.py
inflating: PIL/PSDraw.py
inflating: PIL/FontFile.py
inflating: PIL/ImageMath.py
inflating: PIL/ImageSequence.py
inflating: PIL/PsdImagePlugin.py
inflating: PIL/_imaging.cpython-35m-x86_64-linux-gnu.so
inflating: PIL/IcoImagePlugin.py
inflating: PIL/TiffImagePlugin.py
inflating: PIL/OleFileIO.py
inflating: PIL/JpegImagePlugin.py
inflating: PIL/CurImagePlugin.py
inflating: PIL/PngImagePlugin.py
inflating: PIL/_webp.cpython-35m-x86_64-linux-gnu.so
inflating: PIL/WebPImagePlugin.py
inflating: PIL/_imagingft.cpython-35m-x86_64-linux-gnu.so
inflating: PIL/ImageTk.py
inflating: PIL/_imagingcms.cpython-35m-x86_64-linux-gnu.so
inflating: PIL/FpxImagePlugin.py
inflating: PIL/ImageQt.py
inflating: PIL/PyAccess.py
inflating: PIL/PcdImagePlugin.py
inflating: PIL/.libs/libopenjp2-e366d6b0.so.2.1.0
inflating: PIL/.libs/libwebpdemux-eba3dc32.so.2.0.4
inflating: PIL/.libs/liblcms2-a6801db4.so.2.0.8
inflating: PIL/.libs/liblzma-6cd627ed.so.5.2.4
inflating: PIL/.libs/libpng16-898afbbd.so.16.35.0
inflating: PIL/.libs/libtiff-8a6d997d.so.5.3.0
inflating: PIL/.libs/libjpeg-3fe7dfc0.so.9.3.0
inflating: PIL/.libs/libwebp-8ccd29fd.so.7.0.2
inflating: PIL/.libs/libz-a147dcb0.so.1.2.3
inflating: PIL/.libs/libfreetype-7ce95de6.so.6.16.1
inflating: PIL/.libs/libwebpmux-1c63fe99.so.3.0.2
所以我們可以發現,雖然pip包的名字是Pillow,但是因為安裝後文件夾的名稱是PIL,所以必須用import PIL
。
而我們升級Pillow後,PIL文件夾的內容會被改變。我們重啟python後再用import PIL
時就會得到新版的Pillow。
matplotlib讀圖的方式
為何重新安裝Pillow,matplotlib讀圖時發生的問題就一併解決了呢?
首先去Matplotlib - Image tutorial的頁面查看:
Loading image data is supported by the Pillow library. Natively, matplotlib only supports PNG images. The commands shown below fall back on Pillow if the native read fails.
這句話說明了matplotlib的原生讀圖程式只能讀.png檔;如果是.png以外的檔案,matplotlib則會調用Pillow來讀圖。
這個錯誤是發生在讀.jpg檔時,這代表matplotlib實際上是透過Pillow來讀的。
這也就解釋了為何升級了Pillow,matplotlib的行為就跟著改變了。
參考連結
PIL官網
PIL官網 - What about PIL?
Pillow的pypi頁面
Matplotlib - Image tutorial