Modes and convert()

Pillow · Essentials · 10 min

Mode mismatches cause most save errors and odd colours, for example saving RGBA as JPEG or reading palette PNGs.

An image's mode defines its bands (channels) and bit depth. The common ones:

  • "RGB": 3 x 8-bit; "RGBA": plus an alpha (transparency) band
  • "L": 8-bit grayscale (luminance); "LA": grayscale plus alpha
  • "1": 1-bit black and white, stored one byte per pixel
  • "P": palette mode: each pixel is an index into a colour table of up to 256 entries (GIFs, many PNGs)
  • "CMYK": printing; many JPEGs from print workflows use it
  • "I;16", "I", "F": 16-bit and 32-bit integer and float images, used for scientific and depth data

im.convert(mode) returns a converted copy. RGB to L uses the weights L = R*299/1000 + G*587/1000 + B*114/1000. getbands() lists the bands and split() returns one L image per band.

Typical fixes you'll need:

  • Before saving as JPEG, convert RGBA or P to RGB, because JPEG has no alpha or palette and Pillow raises OSError: cannot write mode RGBA as JPEG.
  • After Image.open on a PNG or GIF, check for "P" mode before doing pixel maths: values are palette indices, not colours. convert("RGB") or convert("RGBA") resolves them.
  • Dropping alpha with convert("RGB") discards transparency, so transparent pixels show whatever colour they stored, often black. Composite onto a background first if appearance matters.

convert("1") dithers by default (Floyd-Steinberg); pass dither=Image.Dither.NONE for a plain threshold.

Example

from PIL import Image

rgba = Image.new("RGBA", (4, 4), (255, 0, 0, 128))     # half-transparent red
print(rgba.mode, rgba.getbands())                       # RGBA ('R', 'G', 'B', 'A')

try:
    rgba.save("out.jpg")
except OSError as e:
    print("error:", e)                                  # cannot write mode RGBA as JPEG
rgba.convert("RGB").save("out.jpg")                     # works

gray = Image.new("RGB", (1, 1), (255, 0, 0)).convert("L")
print(gray.getpixel((0, 0)))                            # 76

pal = Image.new("RGB", (2, 1), (0, 128, 255)).convert("P")
print(pal.mode, pal.getpixel((0, 0)))                   # P <an index, not a colour>
print(pal.convert("RGB").getpixel((0, 0)))              # (0, 153, 255) nearest palette colour

r, g, b, a = rgba.split()
print(a.mode, a.getpixel((0, 0)))                       # L 128

Remember

  • Mode = bands + bit depth: RGB, RGBA, L, 1, P, CMYK, I;16, F
  • convert(mode) returns a converted copy
  • Convert RGBA/P to RGB before saving JPEG
  • P-mode pixels are palette indices, not colours

Watch out for

  • Doing arithmetic on a P-mode image's pixel values as if they were colours or intensities.

Check yourself

  1. Why does saving an RGBA image as JPEG fail?
  2. A GIF opens with mode 'P' and `getpixel((0, 0))` returns 3. What is 3?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: pillow.readthedocs.io/en/stable/handbook/concepts.html#modes