Skip to Content
Course content

338: Generating Thumbnails and Watermarks Programmatically

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

I've seen a lot of developers approach image processing by treating pixels like they're just items in a list. When they first try to build a thumbnail generator, they usually reach for a simple resize function and assume that's the end of the story. But there is a massive difference between resizing an image and generating a thumbnail.

"Resizing is Just Changing the Width and Height"

The most common mistake I see is using a basic resize method—like Image.resize((100, 100)) in Pillow—and expecting the image to look right. If you take a high-res landscape photo (say, 4000x2000) and force it into a 100x100 square, you aren't making a thumbnail; you're squashing your image. Your mountains become needles and your people look like they've been flattened by a steamroller.

from PIL import Image

# The WRONG way: This ignores the aspect ratio
img = Image.open("vacation_photo.jpg")
thumbnail = img.resize((100, 100)) 
thumbnail.save("squashed.jpg")

Beyond the distortion, simple resizing often ignores the "sampling" process. If you just throw away pixels to make the image smaller, you get "aliasing"—those jagged, shimmering edges that make a photo look amateur. You need a resampling filter (like Lanczos) to average the pixels correctly.

Maintaining Proportions with Image.thumbnail

To do this properly, you want to maintain the aspect ratio. Pillow provides a method specifically for this: thumbnail(). Unlike resize(), thumbnail() calculates the largest possible size that fits within your requested dimensions while keeping the image's original proportions. It also modifies the image in place, which is more memory-efficient for batch processing.

from PIL import Image

img = Image.open("vacation_photo.jpg")
# This will not distort the image; it fits it within a 128x128 box
img.thumbnail((128, 128), Image.Resampling.LANCZOS)
img.save("proper_thumb.jpg")

If you absolutely need a perfect square (which is common for profile pictures), don't squash the image. Instead, you should "center crop"—find the shortest side, crop the center to a square, and then thumbnail that. It's a few more lines of code, but it's the difference between a professional app and a broken one.

Avoiding the "Blocky" Watermark

Now, let's talk about watermarks. A common frustration I hear is: "I tried to add my logo, but it has a weird white or black box around it!" This happens because the developer is pasting an RGB image onto another RGB image, ignoring the alpha channel (transparency).

To get a professional, translucent watermark, both your base image and your watermark must be in RGBA mode. You don't just "paste" the image; you use the watermark's own alpha channel as a mask. This tells Python, "Only apply the pixels where the logo actually exists; leave the transparent parts alone."

from PIL import Image

# Load the main image and the watermark
base = Image.open("product_shot.jpg").convert("RGBA")
watermark = Image.open("logo.png").convert("RGBA")

# Scale the watermark to be, say, 20% of the base image width
w_width, w_height = watermark.size
base_width, base_height = base.size
scale_factor = (base_width * 0.2) / w_width
new_size = (int(w_width * scale_factor), int(w_height * scale_factor))
watermark = watermark.resize(new_size, Image.Resampling.LANCZOS)

# Calculate position (bottom right corner with a 20px margin)
position = (base_width - watermark.size[0] - 20, base_height - watermark.size[1] - 20)

# Create a blank layer for the watermark to avoid modifying the base directly
overlay = Image.new("RGBA", base.size, (0, 0, 0, 0))
overlay.paste(watermark, position)

# Composite the two together
result = Image.alpha_composite(base, overlay)
result.convert("RGB").save("watermarked_product.jpg")

I prefer using Image.alpha_composite over a simple paste because it handles the blending of semi-transparent pixels much more naturally. If your logo has a soft glow or a drop shadow, alpha_composite will preserve that; a standard paste often leaves a jagged edge.




📋 Practical Task

Build a Batch Image Processor for an E-commerce Catalog

You have a folder of high-resolution product images. Your goal is to automate the creation of a "Gallery Set" for each image. Write a Python script that performs the following for every .jpg file in a source directory:

  • Create a Square Thumbnail: Generate a 200x200 thumbnail. If the original image is not square, you must center-crop it first so that the final result is a perfect square without any stretching or distortion.
  • Apply a Brand Watermark: Take a provided logo.png (with transparency) and place it in the bottom-right corner of the original image. The logo should be scaled to exactly 15% of the base image's width.
  • Save Outputs: Save the thumbnail in a folder named /thumbnails and the watermarked version in a folder named /watermarked, maintaining the original filenames.

Hint: Remember to handle the conversion between RGB and RGBA modes to ensure your watermark doesn't have a solid background.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.