-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
337: Resizing, Cropping, and Converting Image Formats
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.jpgwith 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.
There are no comments for now.