ImageOps holds the ready made operations that would otherwise be five lines each. All of them return a new image.
from PIL import Image, ImageOps
im = ImageOps.exif_transpose(Image.open("photo.jpg"))
Phone cameras do not rotate the pixels when you turn the phone. They record an orientation tag and leave the pixels alone. Browsers and photo apps honour that tag, so the picture looks right, and then your script produces a sideways thumbnail. exif_transpose applies the rotation for real. Put it immediately after every open that handles photographs.
| Call | What you get |
|---|---|
contain(im, size) | Scaled down to fit inside the box, aspect kept, nothing cropped |
cover(im, size) | Scaled to fill the box, aspect kept, overflow cropped |
fit(im, size, centering=(0.5, 0.5)) | Exactly the box, cropping whatever does not fit |
pad(im, size, color) | Exactly the box, with bars added rather than cropping |
square = ImageOps.fit(im, (400, 400), Image.Resampling.LANCZOS)
fit is what you want for avatars and grid thumbnails, where every tile has to be the same shape.
| Call | What it does |
|---|---|
autocontrast(im, cutoff=0) | Stretches the histogram so the darkest pixel is black and the lightest is white |
equalize(im) | Flattens the histogram. Strong, and often ugly on photographs |
grayscale(im) | The same as im.convert("L") |
invert(im) | A negative |
posterize(im, bits) | Reduces to a few levels per channel |
solarize(im, threshold) | Inverts everything above the threshold |
colorize(im, black, white) | Tints a greyscale image between two colours |
autocontrast with a small cutoff, say 1 or 2, is the single most useful one for scanned pages and dull photographs, because the cutoff ignores the few extreme pixels that would otherwise ruin the stretch.
framed = ImageOps.expand(im, border=10, fill="white")
border can be one number, a pair for left-right and top-bottom, or four numbers.