Pillow
| Language | Python 3 |
|---|---|
| License | MIT-CMU (permissive, HPND-style) |
| Repository | github.com/python-pillow/Pillow |
| Documentation | JPEG in Pillow's format docs |
| Depends on | libjpeg-turbo (bundled in the PyPI wheels) |
| Read / write | yes / yes; baseline and progressive, CMYK, 4:4:4 / 4:2:2 / 4:2:0, custom quantization tables; Exif, ICC, XMP, comments |
Pillow (the maintained fork of PIL) is the most used imaging library for Python. For JPEG it exposes almost every libjpeg-turbo option through keyword arguments of Image.save, reads and writes Exif through Image.getexif(), and supports draft mode: decoding at ½, ¼ or 1/8 scale, which makes thumbnails of large photos very fast.
Pillow doesn’t apply the Exif orientation when it opens a file; call ImageOps.exif_transpose. When saving, metadata isn’t copied unless you pass it (exif=, icc_profile=).
Installation#
pip install pillow
Basic usage: open, fix rotation, resize and save#
from PIL import Image, ImageOps
with Image.open("photo.jpg") as im:
print(im.format, im.size, im.mode) # JPEG (4032, 3024) RGB
im = ImageOps.exif_transpose(im) # apply the Exif orientation
im.thumbnail((1200, 1200)) # resize in place, keep aspect ratio
im.save("out.jpg", quality=85, optimize=True, progressive=True)
How to save a JPEG with a specific quality and chroma subsampling#
im.save(
"out.jpg",
quality=90, # 1-95 recommended; 75 is the default
subsampling=0, # 0 = 4:4:4, 1 = 4:2:2, 2 = 4:2:0 (or "4:4:4", "4:2:2", "4:2:0")
optimize=True, # optimal Huffman tables
progressive=True,
)
When re-saving a JPEG you opened, quality="keep" reuses its quantization tables and subsampling, which keeps the quality close to the original.
How to read Exif data (camera, date, GPS)#
from PIL import Image, ExifTags
with Image.open("photo.jpg") as im:
exif = im.getexif()
print("Camera:", exif.get(ExifTags.Base.Make), exif.get(ExifTags.Base.Model))
print("Orientation:", exif.get(ExifTags.Base.Orientation))
sub = exif.get_ifd(ExifTags.IFD.Exif) # DateTimeOriginal, exposure, ISO live here
print("Taken:", sub.get(ExifTags.Base.DateTimeOriginal))
print("Exposure:", sub.get(ExifTags.Base.ExposureTime))
gps = exif.get_ifd(ExifTags.IFD.GPSInfo)
if gps:
print("Lat:", gps.get(ExifTags.GPS.GPSLatitudeRef), gps.get(ExifTags.GPS.GPSLatitude))
print("Lon:", gps.get(ExifTags.GPS.GPSLongitudeRef), gps.get(ExifTags.GPS.GPSLongitude))
for tag_id, value in exif.items(): # all IFD0 tags with names
print(ExifTags.TAGS.get(tag_id, tag_id), value)
GPS coordinates are (degrees, minutes, seconds) tuples of rationals.
How to keep Exif and the ICC profile when saving#
from PIL import Image, ImageOps
with Image.open("photo.jpg") as im:
icc = im.info.get("icc_profile")
im = ImageOps.exif_transpose(im) # also resets the orientation tag in im.getexif()
im.thumbnail((1600, 1600))
im.save("out.jpg", quality=85, exif=im.getexif(), icc_profile=icc)
To strip metadata, just don’t pass exif and icc_profile.
How to make thumbnails of large JPEGs quickly#
thumbnail calls draft, which tells libjpeg to decode at a reduced scale. A 24-megapixel photo then decodes at 1/8 size:
with Image.open("big.jpg") as im:
im.thumbnail((300, 300), Image.Resampling.LANCZOS)
im.save("thumb.jpg", quality=80)
Call im.draft("RGB", (w, h)) yourself when you need a reduced image without thumbnail.
How to convert PNG with transparency to JPEG#
JPEG has no alpha: paste on a background color, or convert("RGB") makes transparent areas black:
from PIL import Image
with Image.open("logo.png") as im:
im = im.convert("RGBA")
bg = Image.new("RGB", im.size, "white")
bg.paste(im, mask=im.getchannel("A"))
bg.save("logo.jpg", quality=90)
Tips#
- Saving an
RGBA,PorLAimage as JPEG raisesOSError: cannot write mode RGBA as JPEG; convert first. Image.openis lazy: it reads only the header, soim.sizeis cheap. Pixel data is decoded on first access.- For CMYK JPEGs (
im.mode == "CMYK") convert withImageCmsand the embedded profile for accurate colors;im.convert("RGB")is a rough conversion. - Set
Image.MAX_IMAGE_PIXELSto limit decompression-bomb protection to your own threshold.