Pillow
| Language | Python 3 |
|---|---|
| License | MIT-CMU (permissive) |
| Repository | github.com/python-pillow/Pillow |
| Documentation | pillow.readthedocs.io, PNG format notes |
| Depends on | zlib, libjpeg and other codecs (bundled in the binary wheels) |
| Read / write | yes / yes, all color types (16-bit grayscale; 16-bit RGB is reduced to 8 bits), text chunks, APNG |
Pillow is the maintained fork of PIL and the default image library for Python. It opens and saves PNG, JPEG, GIF, WebP, TIFF, BMP, ICO and many more formats, and has functions for resizing, cropping, rotating, drawing, filters and color conversion. Binary wheels for Windows, macOS and Linux include all codecs.
For PNG it reads and writes text chunks, pHYs (DPI), ICC profiles, Exif and transparency, and reads and writes APNG animations. One limitation: 16-bit-per-channel RGB and RGBA images are converted to 8 bits on load. Use pypng or OpenCV when you need full 16-bit color.
Installation#
pip install pillow
Basic usage: open, resize and save a PNG#
from PIL import Image
with Image.open("in.png") as im:
print(im.size, im.mode) # (width, height), e.g. "RGBA", "RGB", "P", "L"
im.thumbnail((800, 800), Image.Resampling.LANCZOS) # fit inside 800x800, keeps aspect ratio
im.save("out.png")
Image.open only reads the header; pixels are decoded on first access. The output format comes from the file extension.
How to get PNG width and height without decoding#
from PIL import Image
with Image.open("in.png") as im:
width, height = im.size
print(width, height, im.mode, im.info.get("dpi"))
How to read and write PNG text chunks#
im.text returns tEXt, zTXt and iTXt chunks as a dict. To write them, pass a PngInfo object:
from PIL import Image
from PIL.PngImagePlugin import PngInfo
with Image.open("in.png") as im:
for key, value in im.text.items():
print(f"{key}: {value}")
meta = PngInfo()
meta.add_text("Title", "Sunset")
meta.add_text("Description", "long text is compressed", zip=True) # zTXt
meta.add_itxt("Author", "Zoë", lang="en") # UTF-8 iTXt
im.save("out.png", pnginfo=meta)
How to make a PNG file smaller#
from PIL import Image
with Image.open("in.png") as im:
# lossless: best zlib settings (slower)
im.save("optimized.png", optimize=True)
# lossy: reduce to a 256-color palette, keeps transparency
im.convert("RGBA").quantize(colors=256).save("palette.png", optimize=True)
How to convert PNG to JPEG#
JPEG can’t store transparency. Paste the image onto a white background:
from PIL import Image
with Image.open("in.png") as im:
im = im.convert("RGBA")
bg = Image.new("RGB", im.size, (255, 255, 255))
bg.paste(im, mask=im.getchannel("A"))
bg.save("out.jpg", quality=85)
How to create an animated PNG (APNG)#
from PIL import Image
frames = [Image.new("RGB", (100, 100), color) for color in ("red", "green", "blue")]
frames[0].save(
"anim.png",
save_all=True,
append_images=frames[1:],
duration=500, # milliseconds per frame
loop=0, # 0 = forever
)
Read frames of an APNG with im.n_frames and im.seek(i), or PIL.ImageSequence.Iterator(im).
Tips#
- Set
Image.MAX_IMAGE_PIXELSto limit the size of untrusted images; Pillow raisesDecompressionBombErrorabove twice the limit. compress_level=1saves much faster than the default6with slightly larger files, useful for temporary files.- Use
im.save(buf, format="PNG")with anio.BytesIOto encode in memory.