PIL
image processing
crop image
Python tutorial
Python Imaging Library

How to crop an image using PIL?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Cropping an image with Pillow is simple once you understand the crop box format. The real mistakes happen around coordinate interpretation, bounds checking, and saving the result correctly. If those three things are handled, cropping becomes a straightforward operation you can reuse in scripts and pipelines.

Use Image.crop with a Four-Value Box

In Pillow, the crop box is:

(left, upper, right, lower)

The right and lower bounds are exclusive, so the output width is right - left and the output height is lower - upper.

python
1from PIL import Image
2
3with Image.open("input.jpg") as image:
4    cropped = image.crop((100, 120, 900, 720))
5    print(cropped.size)  # (800, 600)
6    cropped.save("output.jpg", quality=95)

That tuple is measured from the top-left corner of the image.

Check the Image Size Before Cropping

Hard-coding crop coordinates is fine only if you know the image dimensions in advance. In general, inspect the size first.

python
1from PIL import Image
2
3with Image.open("input.jpg") as image:
4    width, height = image.size
5    print(width, height)

Knowing the size lets you validate that your crop box is sensible before you apply it.

Build a Safe Crop Helper

If coordinates may come from user input or another system, clip them to the image bounds.

python
1from PIL import Image
2
3
4def safe_crop(path_in, path_out, left, upper, right, lower):
5    with Image.open(path_in) as image:
6        width, height = image.size
7
8        left = max(0, left)
9        upper = max(0, upper)
10        right = min(width, right)
11        lower = min(height, lower)
12
13        if right <= left or lower <= upper:
14            raise ValueError("invalid crop box")
15
16        cropped = image.crop((left, upper, right, lower))
17        cropped.save(path_out, quality=95)
18
19
20safe_crop("input.jpg", "output_safe.jpg", 50, 50, 1100, 800)

This prevents negative coordinates or inverted boxes from silently producing bad results.

Center Crop Is a Useful Reusable Pattern

For avatars, thumbnails, and cards, a center crop is often what you want.

python
1from PIL import Image
2
3
4def center_crop(image, target_width, target_height):
5    width, height = image.size
6
7    left = max(0, (width - target_width) // 2)
8    upper = max(0, (height - target_height) // 2)
9    right = min(width, left + target_width)
10    lower = min(height, upper + target_height)
11
12    return image.crop((left, upper, right, lower))
13
14
15with Image.open("portrait.jpg") as image:
16    result = center_crop(image, 512, 512)
17    result.save("portrait_crop.jpg", quality=92)

That gives a predictable crop without needing manual coordinates for every file.

Normalize Orientation First

Phone photos often contain EXIF orientation metadata. If you crop before normalizing orientation, the crop region may be applied to the wrong visual orientation.

python
1from PIL import Image, ImageOps
2
3with Image.open("phone_photo.jpg") as image:
4    normalized = ImageOps.exif_transpose(image)
5    cropped = normalized.crop((200, 200, 1200, 1200))
6    cropped.save("phone_photo_crop.jpg", quality=92)

This is a small step, but it avoids a lot of confusing "my crop is in the wrong place" bugs.

Batch Cropping Follows the Same Rules

Once the crop logic is correct, applying it across many images is just normal file iteration.

python
1from pathlib import Path
2from PIL import Image
3
4input_dir = Path("images_raw")
5output_dir = Path("images_cropped")
6output_dir.mkdir(exist_ok=True)
7
8for path in input_dir.glob("*.jpg"):
9    with Image.open(path) as image:
10        cropped = image.crop((0, 0, 800, 800))
11        cropped.save(output_dir / f"{path.stem}_crop.jpg", quality=90)

Keep output naming stable and avoid repeatedly re-saving the same JPEG in a loop if you care about quality.

Common Pitfalls

  • Forgetting that the crop box is (left, upper, right, lower).
  • Treating the right and lower edges as inclusive instead of exclusive.
  • Applying crop coordinates without checking the image size first.
  • Cropping phone images before handling EXIF orientation.
  • Recompressing JPEGs repeatedly and losing quality unnecessarily.

Summary

  • Use image.crop((left, upper, right, lower)) in Pillow.
  • Right and lower bounds are exclusive.
  • Validate and clip crop coordinates when they are not fully trusted.
  • Normalize EXIF orientation before cropping camera images.
  • Reuse helpers such as center-crop and safe-crop for consistent results.

Course illustration
Course illustration

All Rights Reserved.