[Python] PIL OSError: cannot write mode RGBA as JPEG

使用 python 的 pil 處理圖片,原圖是 png 想另存為 JPG, 顯示錯誤訊息:

  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/PIL/JpegImagePlugin.py", line 632, in _save
    raise OSError(f"cannot write mode {im.mode} as JPEG") from e
OSError: cannot write mode RGBA as JPEG

解法:
https://stackoverflow.com/questions/48248405/cannot-write-mode-rgba-as-jpeg

JPG does not support transparency – RGBA means RedGreenBlueAlpha – Alpha is transparency.

You need to discard the Alpha Channel or save as something that supports transparency – like PNG.

The Image class has a method convert which can be used to convert RGBA to RGB – after that you will be able to save as JPG.

Have a look here: the image class doku

im = Image.open("audacious.png")
rgb_im = im.convert('RGB')
rgb_im.save('audacious.jpg')

上面的解決,不太好用,因為透明的 alpha 會變成黑色,不是一般預期的白色。

請改用下面的解法:
https://stackoverflow.com/questions/9166400/convert-rgba-png-to-rgb-with-pil

Here’s a version that’s much simpler – not sure how performant it is. Heavily based on some django snippet I found while building RGBA -> JPG + BG support for sorl thumbnails.

from PIL import Image

png = Image.open(object.logo.path)
png.load() # required for png.split()

background = Image.new("RGB", png.size, (255, 255, 255))
background.paste(png, mask=png.split()[3]) # 3 is the alpha channel

background.save('foo.jpg', 'JPEG', quality=80)

直接使用上面的 code, 如果是 PNG 檔不會出錯,但遇到 jpg 檔會出錯,錯誤訊息:

background.paste(nim, mask=nim.split()[3])
IndexError: tuple index out of range

  • image.mode: returns a str containing the mode of the data read. Typical values are "RGB" and "L" for RGB and gray-scale images respectively. Modes are presented here.
  • im2.info: which returns a dict containing various information about the image. This is image format specific. For jpg images for example it (possibly) contains fields with keys: dpijfifjfif_densityexif etc. More information about jpg images can be found here.
  • image.getbands(): which returns a tuple (even a 1 element one) containing all different channel present in the data. For a typical RGB image this would be ('R', 'G', 'B') and for a typical gray-scale image would be ('L',).

解法:

if nim.mode == "RGBA":
    background = Image.new("RGB", nim.size, (255, 255, 255))
    background.paste(nim, mask=nim.split()[3])

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *