Resize, thumbnail and crop

Pillow · Essentials · 10 min

Generating previews, normalising dataset images and cutting regions are the most common Pillow jobs.

im.resize((width, height), resample=...) returns an image of exactly that size, distorting it if the aspect ratio differs. Resampling filters live in Image.Resampling:

  • LANCZOS: highest quality for downscaling photos (the old ANTIALIAS name was removed in Pillow 10)
  • BICUBIC (default for most modes), BILINEAR: good general choices
  • NEAREST: no blending; use it for pixel art, masks and label images

im.thumbnail((max_w, max_h)) shrinks the image in place to fit inside the box while keeping its aspect ratio, and never enlarges. It returns None, so don't assign its result. For a copy, call it on im.copy().

ImageOps.contain(im, size) (fit inside), ImageOps.cover (fill the box, may overflow) and ImageOps.fit(im, size) (fill exactly by cropping the centre) cover the other common cases, returning new images.

im.crop((left, upper, right, lower)) takes a box of pixel coordinates; right and lower are exclusive, so the result is right - left wide. Coordinates outside the image are allowed and filled with zeros (black or transparent).

im.reduce(factor) shrinks by an integer factor quickly by averaging blocks, which is handy before a final resize on very large images.

Example

from PIL import Image, ImageOps

im = Image.new("RGB", (1200, 800), "orange")

small = im.resize((300, 200), Image.Resampling.LANCZOS)
print(small.size)                    # (300, 200)

thumb = im.copy()
result = thumb.thumbnail((256, 256))  # in place, keeps aspect ratio
print(result, thumb.size)            # None (256, 171)

square = ImageOps.fit(im, (200, 200))        # centre-crop to fill
print(square.size)                           # (200, 200)
print(ImageOps.contain(im, (200, 200)).size) # (200, 133)

box = (100, 50, 400, 250)            # left, upper, right, lower
crop = im.crop(box)
print(crop.size)                     # (300, 200)
print(im.crop((1100, 0, 1300, 10)).getpixel((150, 5)))   # (0, 0, 0) outside

Remember

  • resize gives an exact size; pick Image.Resampling.LANCZOS for quality downscaling
  • thumbnail shrinks in place, keeps aspect ratio and returns None
  • ImageOps.fit/contain/cover handle common fitting rules
  • crop((left, upper, right, lower)) uses exclusive right/lower

Watch out for

  • Writing thumb = im.thumbnail(size) and getting None.

Check yourself

  1. A 1000x500 image gets `im.thumbnail((200, 200))`. What is `im.size` afterwards?
  2. What size is `im.crop((10, 20, 110, 70))`?

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

Official docs: pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.resize