Python Snippets

Parallel Image Thumbnail Generator with ThreadPoolExecutor

This snippet demonstrates how to generate thumbnails for multiple images in parallel using Python’s ThreadPoolExecutor. It’s useful for batch processing images for websites or applications where performance matters.

import os
from concurrent.futures import ThreadPoolExecutor
from PIL import Image
from pathlib import Path

def generate_thumbnail(image_path, output_dir, size=(128, 128)):
    """Generate thumbnail for a single image"""
    try:
        img = Image.open(image_path)
        img.thumbnail(size)
        output_path = Path(output_dir) / f"thumb_{Path(image_path).name}"
        img.save(output_path)
        print(f"Generated thumbnail for {image_path}")
        return True
    except Exception as e:
        print(f"Error processing {image_path}: {str(e)}")
        return False

def process_images_parallel(image_paths, output_dir, max_workers=4):
    """Process multiple images in parallel using thread pool"""
    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        futures = []
        for img_path in image_paths:
            future = executor.submit(
                generate_thumbnail,
                img_path,
                output_dir
            )
            futures.append(future)
        
        # Wait for all tasks to complete
        results = [f.result() for f in futures]
        return sum(results)  # Count of successful thumbnails

if __name__ == "__main__":
    # Example usage
    input_dir = "images"  # Directory containing source images
    output_dir = "thumbnails"  # Directory to save thumbnails
    
    # Create output directory if it doesn't exist
    os.makedirs(output_dir, exist_ok=True)
    
    # Get all JPEG and PNG images in input directory
    image_paths = [
        os.path.join(input_dir, f) 
        for f in os.listdir(input_dir) 
        if f.lower().endswith(('.jpg', '.jpeg', '.png'))
    ]
    
    if not image_paths:
        print("No images found in the input directory")
    else:
        success_count = process_images_parallel(image_paths, output_dir)
        print(f"Successfully generated {success_count}/{len(image_paths)} thumbnails")

Explanation

This code solves the common problem of efficiently generating thumbnails for multiple images. Here’s why it’s useful:

  1. Parallel Processing: Uses ThreadPoolExecutor to process multiple images simultaneously, significantly reducing total processing time for large batches.

  2. Error Handling: Each thumbnail generation is wrapped in try-except to prevent one failed image from stopping the entire batch.

  3. Modern Python Features: Uses pathlib for path manipulation and type hints for better code clarity.

  4. Flexible Configuration: You can easily adjust the thumbnail size or number of worker threads.

How to Use

  1. Prerequisites:
    • Install Pillow: pip install pillow
    • Create an “images” directory and add some JPEG/PNG files
  2. Running:
    • Save the script (e.g., thumbnail_generator.py)
    • Run with Python: python thumbnail_generator.py
  3. Customization:
    • Change thumbnail size by modifying the size parameter
    • Adjust parallelism by changing max_workers
    • Modify file extensions in the list comprehension to support other image types

The script will create thumbnails in the specified output directory with “thumb_” prefix added to each filename.