Python Image Optimization Script

Read a folder recursively

So new assignment!

Write a Python script that optimizes images for web performance without losing quality, converts them to RGB, and calculates the space saved. The script should accept an input folder, read the folder recursively, and place the optimized images in a different folder while preserving the original folder structure. The script should also make the optimized filenames web safe.

Let's try this!

To optimize images for web performance without losing quality, we can use the Pillow library for image processing, and the slugify library to make the filenames web safe. Here's a script with inline comments for a brief explanation:

import os
import sys
from PIL import Image, ImageFile
from slugify import slugify

# Optimize a single image and save it to the output folder
def optimize_image(image_path, output_folder):
    # Set LOAD_TRUNCATED_IMAGES to True
    ImageFile.LOAD_TRUNCATED_IMAGES = True

    # Open the image
    img = Image.open(image_path)

    # Convert the image to RGB
    img_rgb = img.convert('RGB')

    # Save image with optimized settings
    output_path = os.path.join(output_folder, slugify(os.path.basename(image_path)))
    img_rgb.save(output_path, "JPEG", optimize=True, quality=85)

    # Return the original and optimized image file sizes
    original_size = os.path.getsize(image_path)
    optimized_size = os.path.getsize(output_path)

    return original_size, optimized_size

# Recursively process the input folder, optimize images, and save them in the output folder
def process_folder(input_folder, output_folder):
    total_original_size = 0
    total_optimized_size = 0

    for root, dirs, files in os.walk(input_folder):
        for file in files:
            if file.lower().endswith(('.jpg', '.jpeg', '.png')):
                input_path = os.path.join(root, file)
                relative_path = os.path.relpath(root, input_folder)
                output_subfolder = os.path.join(output_folder, relative_path)
                os.makedirs(output_subfolder, exist_ok=True)
                original_size, optimized_size = optimize_image(input_path, output_subfolder)
                total_original_size += original_size
                total_optimized_size += optimized_size

    return total_original_size, total_optimized_size

# Format file size in a human-readable way
def format_size(size):
    if size < 1024:
        return f"{size} B"
    elif size < 1024 * 1024:
        return f"{size / 1024:.2f} KB"
    else:
        return f"{size / (1024 * 1024):.2f} MB"

# Main entry point of the script
if __name__ == "__main__":
    if len(sys.argv) < 3:
        print("Usage: python optimize_images.py INPUT_FOLDER OUTPUT_FOLDER")
        sys.exit(1)

    input_folder = sys.argv[1]
    output_folder = sys.argv[2]

    original_size, optimized_size = process_folder(input_folder, output_folder)
    saved_size = original_size - optimized_size

    print(f"Image optimization complete!\n"
          f"Original size: {format_size(original_size)}\n"
          f"Optimized size: {format_size(optimized_size)}\n"
          f"Space saved: {format_size(saved_size)}")

Here's a detailed explanation of the script:

  1. Import the necessary modules: os, sys, Image, ImageFile from PIL, and slugify.

  2. Define the optimize_image function, which takes an image file path and an output folder as arguments. This function opens the image, converts it to RGB, optimizes it, and saves it to the output folder with a web-safe filename. It also returns the original and optimized image file sizes.

  3. Define the process_folder function, which takes an input folder and an output folder as arguments. This function recursively processes the input folder, optimizes images, and saves them in the output folder. It maintains the original folder structure by calculating the relative path and creating corresponding subfolders in the output folder. The function accumulates the total original and optimized image file sizes and returns them at the end.

  4. Define the format_size function, which takes a file size in bytes as an argument and returns a human-readable string representing the file size in bytes, kilobytes, or megabytes.

  5. Define the main entry point of the script (if __name__ == "__main__":). This section checks for the correct number of command-line arguments and retrieves the input folder and output folder. It calls the process_folder function to process the images and calculate the original size, optimized size, and space saved. Finally, it prints the results in a human-readable format.

To run this script, first install the required libraries using pip:

pip install pillow python-slugify

Then, run the script from the command line, providing the input and output folders as arguments:

python optimize_images.py input_folder output_folder

The script will optimize the images in the input folder, convert them to RGB, save them in the output folder with the same folder structure, and display the total original size, optimized size, and space saved.

Did you find this article valuable?

Support Theo van der Sluijs by becoming a sponsor. Any amount is appreciated!