1 The Problem
We want a batch image resizer: point it at a folder, and it resizes every image to fit within a target size — keeping each image’s proportions so nothing is squashed — saving the results to an output folder. It teaches batch processing and using a real third-party library (Pillow).
2 How to Think About It
Think about resizing without distortion, before any code:
3 The Build — explained part by part
Here is the complete resizer. It uses Pillow (pip install Pillow). The proportion maths is pulled out so we can test it without real images. Each part is explained below.
from pathlib import Path
from PIL import Image # pip install Pillow
def fit_size(width, height, max_size):
# Scale so the longest side equals max_size, keeping proportions.
scale = max_size / max(width, height)
if scale >= 1:
return (width, height) # already small enough
return (round(width * scale), round(height * scale))
def resize_folder(input_dir, output_dir, max_size=800):
out = Path(output_dir)
out.mkdir(exist_ok=True)
for path in Path(input_dir).glob("*.jpg"):
image = Image.open(path)
new_size = fit_size(image.width, image.height, max_size)
resized = image.resize(new_size)
resized.save(out / path.name)
if __name__ == "__main__":
resize_folder("photos", "resized", 800)
print("Done resizing.")
Image.open loads a picture; .resize changes its size; .save writes it out.fit_size(width, height, max_size) — the maths, isolated so it is testable.
scale = max_size / max(width, height) finds the factor that makes the longest side equal max_size.if scale >= 1: return (width, height) — if the image is already smaller than the target, leave it alone — never blow images up.
round(width * scale), round(height * scale) — apply the same scale to both dimensions, so proportions are preserved and the image is not squashed.
resize_folder — the batch loop: find every
.jpg, compute its new size, resize, and save to the output folder.
scale >= 1).4 Test & Prove Each Part
Image files are awkward to test, so we test the proportion maths — the part most likely to have bugs — without any real images.
def fit_size(width, height, max_size):
scale = max_size / max(width, height)
if scale >= 1:
return (width, height)
return (round(width * scale), round(height * scale))
def test_scales_down():
"""A 1600x800 image fits to 800 on its longest side."""
assert fit_size(1600, 800, 800) == (800, 400)
def test_small_unchanged():
"""An image already small enough is unchanged."""
assert fit_size(400, 300, 800) == (400, 300)
def test_ratio_preserved():
"""The width-to-height ratio survives resizing."""
w, h = fit_size(1000, 500, 500)
assert w / h == 1000 / 500
Run with pytest -v. By isolating fit_size, we test the crucial proportion logic with plain numbers — no image files needed. The ratio-preserved test guarantees images never come out squashed.
5 The Interface
Call
resize_folder("photos", "resized", 800)Result
resized/photo1.jpg (was 4000x3000, now 800x600)6 Run It & Automate It
pip install Pillowpython3 resize.pyPut
.jpg files in a photos/ folder; resized copies appear in resized/.Jenkins runs the tests automatically — each line explained below.
Done resizing.
# resized/ now holds proportionally-shrunk copies of every imageModuleNotFoundError: No module named 'PIL'.pip install Pillow.fit_size to scale both dimensions by the same factor.// Jenkinsfile — automated tests on every change.
pipeline {
agent any
stages {
stage('Get the code') { steps { checkout scm } } // download the code
stage('Install') { steps { sh 'python3 -m venv venv && . venv/bin/activate && pip install pytest' } } // test tool
stage('Test') { steps { sh '. venv/bin/activate && pytest -v' } } // run every test
}
post {
success { echo 'All tests passed.' }
failure { echo 'A test failed — look above.' }
}
}
- More formats. Handle PNG and others, not just JPG. (Teaches: globbing multiple patterns.)
- Thumbnails. Also make tiny square crops. (Teaches: cropping.)
- Progress. Show how many images processed. (Teaches: progress reporting.)