Python Script to Resize (Shink) Images in a Folder of JPGs


I had taken a load of photos on holiday and wanted to share them on a forum, but unfortunately the forum had a 12MB limit and some of my photos were bigger. So I asked AI to create a script that would resize the photos in the folder to 75% original size (keeping aspect ratio the same) and using 90% quality, and put the reduced size images in a new folder called "percent75". Worked a treat!

"""
resize_images.py

Resizes all images in a folder to 75% of their original size (keeping
aspect ratio), saves them at quality 90, into a new subfolder called
"percent75", with "r75" appended to the filename.

Does NOT look in subfolders - only the top-level folder you point it at.

Requirements:
    pip install Pillow

Usage:
    1. Edit the SOURCE_FOLDER path below to point at your "New folder".
    2. Run:  python resize_images.py

You can also just run it and it will ask you to paste a folder path
if you leave SOURCE_FOLDER as None.
"""

from pathlib import Path
from PIL import Image

# ---- SETTINGS -------------------------------------------------------

# Set this to your folder, e.g. r"C:\Users\YourName\Desktop\New folder"
# Leave as None to be asked for it when you run the script.
SOURCE_FOLDER = None

RESIZE_PERCENT = 75          # e.g. 75 means 75% of original size
QUALITY = 90                 # JPEG/WEBP quality (1-100)
OUTPUT_FOLDER_NAME = "percent75"
SUFFIX = "r75"

# Which file extensions to treat as images
IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".png", ".webp", ".bmp", ".tiff", ".tif"}

# ----------------------------------------------------------------------


def resize_images(source_folder: Path):
    source_folder = Path(source_folder)

    if not source_folder.is_dir():
        print(f"Error: '{source_folder}' is not a valid folder.")
        return

    output_folder = source_folder / OUTPUT_FOLDER_NAME
    output_folder.mkdir(exist_ok=True)

    # Only files directly inside source_folder (no subfolders)
    image_files = [
        f for f in source_folder.iterdir()
        if f.is_file() and f.suffix.lower() in IMAGE_EXTENSIONS
    ]

    if not image_files:
        print(f"No image files found directly inside '{source_folder}'.")
        return

    print(f"Found {len(image_files)} image(s). Resizing to {RESIZE_PERCENT}% "
          f"at quality {QUALITY}...\n")

    scale = RESIZE_PERCENT / 100.0
    processed = 0
    skipped = 0

    for file_path in image_files:
        try:
            with Image.open(file_path) as img:
                new_width = max(1, round(img.width * scale))
                new_height = max(1, round(img.height * scale))

                resized = img.resize((new_width, new_height), Image.LANCZOS)

                new_name = f"{file_path.stem}{SUFFIX}{file_path.suffix}"
                out_path = output_folder / new_name

                save_kwargs = {}
                ext = file_path.suffix.lower()

                if ext in (".jpg", ".jpeg"):
                    # JPEG has no alpha channel - flatten if needed
                    if resized.mode in ("RGBA", "P"):
                        resized = resized.convert("RGB")
                    save_kwargs["quality"] = QUALITY
                    save_kwargs["optimize"] = True
                elif ext == ".webp":
                    save_kwargs["quality"] = QUALITY
                elif ext == ".png":
                    # PNG is lossless; "quality" doesn't really apply,
                    # but we can still optimize compression.
                    save_kwargs["optimize"] = True
                # bmp/tiff: no quality setting needed

                resized.save(out_path, **save_kwargs)

                orig_size_mb = file_path.stat().st_size / (1024 * 1024)
                new_size_mb = out_path.stat().st_size / (1024 * 1024)
                print(f"  {file_path.name}  ->  {out_path.name}  "
                      f"({orig_size_mb:.2f}MB -> {new_size_mb:.2f}MB)")

                processed += 1

        except Exception as e:
            print(f"  Skipped '{file_path.name}': {e}")
            skipped += 1

    print(f"\nDone. {processed} image(s) resized into: {output_folder}")
    if skipped:
        print(f"{skipped} file(s) were skipped due to errors.")


if __name__ == "__main__":
    folder = SOURCE_FOLDER
    if not folder:
        folder = input("Enter the full path to your image folder: ").strip().strip('"')
    resize_images(Path(folder))

Comments