Skip to Content
Course content

337: Resizing, Cropping, and Converting Image Formats

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I see this one all the time when people start automating their image pipelines. You've got a folder full of PNGs, you want them to be JPEGs to save space, and you want them all to be a standard size. You write a few lines of Pillow code, hit run, and suddenly your terminal is screaming at you.

from PIL import Image

img = Image.open("user_avatar.png")
img = img.resize((200, 200))
img.save("user_avatar.jpg")

# Result: OSError: cannot write mode RGBA as JPEG

The JPEG Transparency Trap

The problem here is that PNGs often have an alpha channel (the 'A' in RGBA), which handles transparency. JPEGs don't know what transparency is. When you tell Pillow to save an RGBA image as a JPEG, it doesn't just "guess" what the background should be; it throws a fit and crashes.

To fix this, you have to explicitly convert the image mode to RGB. But if you just call .convert("RGB") on a transparent image, you might end up with a weird black background where the transparency used to be. The professional way to handle this is to create a solid background canvas and paste your image on top of it.

from PIL import Image

img = Image.open("user_avatar.png")

# Create a white background image the same size as the original
background = Image.new("RGB", img.size, (255, 255, 255))

# Paste the original image using its own alpha channel as a mask
background.paste(img, mask=img.split()[3]) 

background.save("user_avatar.jpg")

By splitting the image, img.split()[3] gives us the alpha channel. We use that as a mask so Pillow knows exactly which pixels are transparent and should show the white background.

Stopping the Squish

Next, let's talk about resize(). In the broken example above, I used img.resize((200, 200)). If the original image was a rectangle, that code just crushed it into a square, making everyone look like they're in a funhouse mirror. I hate seeing distorted images in a production app.

If you want to maintain the aspect ratio, you have two real choices: thumbnail() or manual math. thumbnail() is great because it modifies the image in-place and ensures the image fits within the dimensions you provide without stretching it.

img = Image.open("landscape.jpg")
img.thumbnail((800, 800)) # It will be 800px on its longest side, keeping the ratio
img.save("landscape_thumb.jpg")

Precision Cropping

Sometimes a thumbnail isn't enough; you need a specific crop (like a square profile picture). Pillow's crop() method takes a tuple: (left, upper, right, lower). It's a bit unintuitive at first because it's not (x, y, width, height)β€”it's the coordinates of the box.

I usually calculate the center of the image first so I can crop a perfect square from the middle, regardless of whether the original was portrait or landscape:

img = Image.open("photo.jpg")
width, height = img.size

# Find the smallest dimension to make a square
min_dim = min(width, height)

left = (width - min_dim) / 2
top = (height - min_dim) / 2
right = (width + min_dim) / 2
bottom = (height + min_dim) / 2

img = img.crop((left, top, right, bottom))

Now you have a perfectly centered square that's ready to be resized and saved without any distortion.




πŸ“‹ Practical Task

Build a Social Media Image Standardizer

You've been tasked with creating a script that takes a raw image and prepares it for a user profile. Your script must perform the following sequence of operations on an image file named raw_input.png:

  • Convert: Ensure the image is in RGB mode (handle the transparency by placing it on a white background).
  • Center Crop: Crop the image into a perfect square based on the shortest side.
  • Resize: Resize that square to exactly 300x300 pixels.
  • Export: Save the final result as profile_final.jpg with an optimized quality setting of 85.

Test your script with a PNG that has a transparent background to ensure your conversion logic is working correctly.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.