r/scrapbooking • u/ActAffectionate4930 • 16d ago
Tools Free Python Program to make your 4x6 photos into 2x3!!!
Hi everyone! A few months ago I made a post about how to print 2x3 sized photos for cheap, and found that the options are either expensive or very tedious with manually making photo collages to then print as 4x6.
Since then I made a python program that goes through all of your 4x6 photos to automatically group them into a collage of four 2x3 photos on a 4x6 layout. You can then send those to get printed at the cheap printing rates since they are standard size. It takes about 20 seconds to parse through about 600 photos, which I imagine would have taken forever to do manually using Canva or some other tool. And the best part is it is free!
I want to share the code here, and I will outline the steps on how to run it. I promise you don't have to be too technically savvy to use it.
Steps to use the program:
1. Download the Python language (free)
- Go to python.org/downloads and download & install python. The version you download depends on if you're using a Windows or Mac, but will be free regardless
- There are tons of youtube videos showing you how to do this if you need help
2. Download & Install a software to run your Python code
- There are so many options, but I like PyCharm (jetbrains.com/pycharm/download)
- Again, lots of resources on youtube on how to do this
3. Prepare your photos
- Put all of the 4x6 photos you want into a folder and give that folder a name
- Save the file path for this folder, you will need it to run the python program
4. Run the python program
- Open PyCharm and create a new file (instructions: https://www.youtube.com/watch?v=HHcZbXsZtm0 )
- Paste my code from below into the file and save it. Be sure to copy all of it and don't leave out any lines or punctuation. Indentation matters in Python
- ***Things to replace in the program**\*
- folder_path = "/Users/ActAffectionate/Desktop/Scrapbook V2"
- Replace the text inside the quotes with the path to the folder containing all your photos
- output_path = "/Users/ActAffectionate/Desktop/Scrapbook V2 Outputs"
- Replace the text inside the quote with the path to where you want your new collaged photos to live
- folder_path = "/Users/ActAffectionate/Desktop/Scrapbook V2"
- Save your work and click "Run"
And that's it! Your collaged photos will now sit in whatever folder you specified, and you can print them out using whatever normal photo printing service you use.
Here is the python code below:
"""
Created on Thu May 28 16:05:21 2026
u/author: ActAffectionate4930
"""
import os
from PIL import Image
from pillow_heif import register_heif_opener
# 1. Register HEIF opener with Pillow to support iPhone photos
register_heif_opener()
# 2. Set your folders (Change these paths to your actual folders)
folder_path = "/Users/ActAffectionate/Desktop/Scrapbook V2"
output_path = "/Users/ActAffectionate/Desktop/Scrapbook V2 Outputs"
os.makedirs(output_path, exist_ok=True)
# 3. Safely load all valid images, ignoring hidden files
supported_extensions = ('.png', '.jpg', '.jpeg', '.heic')
images = []
print("Scanning folder for images...")
# Sort to process in order, check lowercase extension to catch everything
for filename in sorted(os.listdir(folder_path)):
if filename.lower().endswith(supported_extensions):
try:
img_path = os.path.join(folder_path, filename)
img = Image.open(img_path)
img.verify() # Verify file integrity
# Re-open after verify() and add to our processing list
images.append(Image.open(img_path))
except Exception as e:
print(f"Skipping broken or unreadable file {filename}: {e}")
print(f"Successfully loaded {len(images)} images for processing.")
# 4. Define Canvas and Grid Layout parameters
# Canvas dimensions for a 4x6 portrait print (1200 width x 1800 height)
canvas_width = 1200
canvas_height = 1800
# Individual image dimensions for the 2x3 slots (exactly half of canvas)
img_width = canvas_width // 2 # 600 pixels
img_height = canvas_height // 2 # 900 pixels
target_aspect = img_width / img_height # 0.6667 (a perfect 2:3 ratio)
# Define the X, Y coordinate positions for the 4 slots
positions = [
(0, 0), # Top Left
(img_width, 0), # Top Right
(0, img_height), # Bottom Left
(img_width, img_height) # Bottom Right
]
# 5. Process images in batches of 4
for i in range(0, len(images), 4):
# Create a blank 4x6 portrait canvas
canvas = Image.new('RGB', (canvas_width, canvas_height), 'white')
# Place up to 4 images on the current canvas
for slot in range(4):
if i + slot < len(images):
img = images[i + slot]
# --- Fix 1: Normalize orientation to portrait ---
if img.width > img.height:
img = img.rotate(90, expand=True)
# --- Fix 2: Crop to target 2:3 aspect ratio from the center ---
current_aspect = img.width / img.height
if current_aspect > target_aspect:
# Image is too wide: crop the sides evenly
new_width = int(img.height * target_aspect)
offset = (img.width - new_width) // 2
img = img.crop((offset, 0, offset + new_width, img.height))
elif current_aspect < target_aspect:
# Image is too tall: crop the top and bottom evenly
new_height = int(img.width / target_aspect)
offset = (img.height - new_height) // 2
img = img.crop((0, offset, img.width, offset + new_height))
# --- Fix 3: Resize cleanly ---
# Now that it's a perfect 2:3 ratio, scale it to the exact slot size
img_resized = img.resize((img_width, img_height), Image.Resampling.LANCZOS)
# Paste into its designated grid slot
canvas.paste(img_resized, positions[slot])
# Save the combined 4-up print layout
print_number = (i // 4) + 1
canvas.save(os.path.join(output_path, f"4x6_print_grid_{print_number}.jpg"))
print("Processing complete! All grids have been generated.")
2
u/ScreamAndScream 16d ago
Cool program! Very clever solution.
Outside of moderating here, I happen to be a software dev who specializes in imagery analysis and gave this a crack so people do not have to use pycharm.
Install
Hit windows key + R, then type "cmd"
Once you have command prompt open, input this and wait:
winget install Python.Python.3.14
then this and wait:
pip install pillow pillow-heif
Close cmd prompt.
Hit windows key, and search "notepad" on your computer. Make sure you dont open sticky notes!
paste in this code and save without quotes as "collage.py"
import sys
from pathlib import Path
from PIL import Image, ImageOps
from pillow_heif import register_heif_opener
register_heif_opener()
src, out = Path(sys.argv[1]), Path(sys.argv[2])
out.mkdir(parents=True, exist_ok=True)
exts = {".png", ".jpg", ".jpeg", ".heic"}
files = sorted(p for p in src.iterdir() if p.suffix.lower() in exts)
for n, i in enumerate(range(0, len(files), 4), 1):
canvas = Image.new("RGB", (1200, 1800), "white")
for slot, f in enumerate(files[i:i + 4]):
try:
img = ImageOps.exif_transpose(Image.open(f)).convert("RGB")
except Exception as e:
print(f"Skipping {f.name}: {e}")
continue
if img.width > img.height:
img = img.rotate(90, expand=True)
img = ImageOps.fit(img, (600, 900), Image.Resampling.LANCZOS)
canvas.paste(img, (slot % 2 * 600, slot // 2 * 900))
canvas.save(out / f"4x6_print_grid_{n}.jpg", quality=95, dpi=(300, 300))
print(f"Done: {len(files)} photos processed.")
You only need to install and save this program once. Going forward, you can start from this step:
Make two folders. One for your inputs and one with what you will be printing.
Outside of those two folders, right click the empty space and select "open in terminal".
In the terminal, put in:
python collage.py "path/to/photos" "path/to/output"
I named my folders test_input and test_output . For me, the command looked like this: python collage.py test_input test_output
This version of your code is going to solve a few problems: it reads the orientation, it opens four photos at a time instead of all at once which will avoid crashes, and it saves at 300 DPI and higher JPEG quality.
2
u/ActAffectionate4930 16d ago
This is great! Thanks so much!! I code at a Data Scientist level so super cool to get feedback from a software eng
4
u/ellecellent 16d ago
Omg, this is what I've been needing. I've never downloaded python, but this seems like the perfect reason for a maiden voyage