Photo Contact Sheet Builder
Build a Python command-line tool that turns a folder of photos into a contact sheet: one image showing small thumbnails of all the photos in a grid, each labeled with its file name, like the sheets photographers use to preview a roll of film. You choose the number of columns, the thumbnail size and the background color, and large folders are split into several pages. It uses Pillow, Python's main image library, keeps each photo's proportions, turns phone photos the right way up, and skips files that aren't images. It's a fun first project with images: you'll learn Pillow, reading folders with pathlib, grid layout math, command-line options with argparse, and testing.
- Estimate
- ~8h
- Steps
- 6
- Completed by
- 0
- Proposed by
- codeseed.app
Pillow · argparse · pathlib
Project roadmap
- 01
Define the command
~1hWrite README.md showing the usage: contact_sheet.py PHOTOS_FOLDER --columns 5 --thumb 200 --out sheet.jpg, with the other options (background color, per page limit and label font size) and an example result. Create a virtual environment and install Pillow and pytest. Done when: README.md describes every option.
- 02
Find the images
~1hUse argparse for the options and validate them (columns 1 to 20, thumbnail size 50 to 1,000). List the files in the folder with pathlib, sorted by name, and keep only those Pillow can open, checked with Image.open and verify(). Report skipped files. Done when: the tool prints the images it will use and the ones it skipped.
- 03
Make thumbnails
~1.5hFor each image, apply ImageOps.exif_transpose so phone photos are the right way up, convert it to RGB, and shrink it with thumbnail() so it fits the thumbnail square while keeping its proportions. Done when: tall, wide and rotated phone photos all produce correctly oriented, undistorted thumbnails.
- 04
Build the sheet
~2hCalculate the number of rows and the sheet size, including margins and space for a label under each thumbnail. Create the sheet with Image.new, paste each thumbnail centered in its cell, and write the file name below it with ImageDraw.text, shortening long names with …. Save as JPEG or PNG based on the output extension. Done when: a folder of photos becomes one tidy, labeled grid image.
- 05
Handle large folders and errors
~1hWith --per-page, split big folders into sheet-1.jpg, sheet-2.jpg and so on. Skip images that fail to load with a warning, and stop with a clear message when the folder doesn't exist or has no images. Don't overwrite existing output files unless --force is given. Done when: a folder with 60 images and one broken file produces several pages and a warning.
- 06
Test it
~1.5hWrite pytest tests that create small test images in a temporary folder: the layout math (rows and pages), thumbnail sizes keeping proportions, label shortening, skipping non-images, and the output file's size. Done when: pytest passes.
Resources
Ready to build this?
Get a GitHub repo and start building. Your AI reviewer checks each step as you go.
Tech stack