DEV Community

Cover image for Image Processing Script: PNG Converter and Resizer
Muhammad Usman
Muhammad Usman

Posted on

Image Processing Script: PNG Converter and Resizer

Image Processing Script:

This Python script allows you to automatically process images by ensuring transparency, cropping unused spaces, resizing to fit a 2:1 canvas, and cleaning up the filenames. Here's what it does:

  • Transparency Handling: Ensures that images are in PNG format with transparency. If the image has a white background, it's replaced with transparency.

  • Space Removal: Removes spaces from image filenames and replaces them with underscores (_).

  • Filename Shortening: If a filename is too long, it truncates it to a maximum length (30 characters).

  • Cropping: The script crops any extra unused space around the image.

  • Resizing: Resizes the image to fit a 2:1 canvas (e.g., 400x200), ensuring the aspect ratio is preserved and the image is centered on the canvas.

  • Logs Skipped Images: If any image is skipped due to being unsupported, empty, or fully transparent, it logs the skipped filenames.

Dependencies:

Pillow (Python Imaging Library fork)
You can install it with:
pip install Pillow

How to Use:

Place your images in a folder (e.g., images).
Run the script, and it will automatically process and save the images in a folder (e.g., edited_images).
The processed images will be resized, centered, and saved in PNG format.
Note:
The script ensures that only valid images are processed and any issues with unsupported formats or transparency are logged in the console.

Here the script started:

from PIL import Image
import os

def ensure_transparency(image):
    # Convert the image to RGBA (if it's not already in that mode)
    if image.mode != 'RGBA':
        image = image.convert('RGBA')

    # If the image doesn't have a transparent background, add one
    if image.getchannel('A').getextrema()[0] != 0:
        # Replace the white or non-transparent background with transparency
        image = image.convert('RGBA')
        image_data = image.getdata()
        new_data = []
        for item in image_data:
            # Change all white (also shades of whites) pixels to transparent
            if item[0] in range(240, 256) and item[1] in range(240, 256) and item[2] in range(240, 256):
                new_data.append((255, 255, 255, 0))  # Replace white with transparency
            else:
                new_data.append(item)
        image.putdata(new_data)
    return image

def process_image(input_path, output_path, target_width, target_height):
    # Open the image
    try:
        image = Image.open(input_path)
    except IOError:
        print(f"Error opening {input_path}, skipping...")
        return None

    # Ensure the image is PNG and has transparency
    image = ensure_transparency(image)

    # Get the bounding box of the non-empty region (crops the unused space)
    bbox = image.getbbox()

    if bbox is None:
        print(f"Image {input_path} has no content or is fully transparent, skipping...")
        return None

    # Crop the image using the bounding box
    cropped_image = image.crop(bbox)

    # Get the dimensions of the cropped image
    img_width, img_height = cropped_image.size
    aspect_ratio = target_width / target_height

    # Calculate new dimensions to fit the image within the 2:1 aspect ratio
    if img_width / img_height > aspect_ratio:
        new_width = target_width
        new_height = int(new_width * (img_height / img_width))  # Scale based on width
    else:
        new_height = target_height
        new_width = int(new_height * (img_width / img_height))  # Scale based on height

    # Resize the image to fit into the 2:1 canvas without cutting
    resized_image = cropped_image.resize((new_width, new_height), Image.Resampling.LANCZOS)

    # Create a new blank canvas with the target size
    final_image = Image.new("RGBA", (target_width, target_height), (255, 255, 255, 0))  # Transparent background

    # Calculate the position to paste the resized image in the center
    x_offset = (target_width - new_width) // 2
    y_offset = (target_height - new_height) // 2

    # Paste the resized image onto the canvas
    final_image.paste(resized_image, (x_offset, y_offset))

    # Save the final image as PNG
    final_image.save(output_path, 'PNG')

def shorten_filename(filename, max_length=30):
    """
    Shortens the filename if it's too long, keeping the extension intact.
    """
    name, ext = os.path.splitext(filename)

    # If the filename is longer than the maximum length, truncate it
    if len(name) > max_length:
        name = name[:max_length]

    return f"{name}{ext}"

def batch_process_images(input_directory, output_directory, target_width, target_height):
    # Create the "edited_images" directory if it doesn't exist
    if not os.path.exists(output_directory):
        os.makedirs(output_directory)

    # Track if any image is missed
    missed_images = []

    # Loop through all images in the input directory
    for filename in os.listdir(input_directory):
        if filename.lower().endswith(('.jpg', '.jpeg', '.png')):  # Check image file types
            image_path = os.path.join(input_directory, filename)
            print(f"Processing: {filename}")

            # Remove spaces and make the filename more descriptive
            new_filename = filename.replace(" ", "_")  # Replace spaces with underscores
            new_filename = shorten_filename(new_filename, max_length=30)  # Shorten the filename if necessary

            # Define the output path for each processed image
            output_path = os.path.join(output_directory, new_filename)

            # Process the image (ensure transparency, crop, and resize)
            result = process_image(image_path, output_path, target_width, target_height)

            # If the result is None, the image was skipped
            if result is None:
                missed_images.append(filename)
        else:
            # If the image file doesn't have a valid extension, skip it and log the name
            missed_images.append(filename)

    # Print the missed images if any
    if missed_images:
        print("\nThe following images were skipped:")
        for img in missed_images:
            print(f" - {img}")

# Define the input and output directories
input_directory = './images'  # Input folder where unedited images are stored
output_directory = './edited_images'  # Output folder where edited images will be saved

# Call the function to process images
batch_process_images(input_directory, output_directory, 500, 250)  # 2:1 ratio (400x200)
Enter fullscreen mode Exit fullscreen mode

Top comments (0)