This project uses Python and Pillow to build batch image collages and solves common multi-image composition issues such as incorrect file ordering, awkward left-aligned final rows, and degraded output quality. The script supports grid layouts, automatic centering for the last row, and DPI preservation. Keywords: Python image collage, Pillow, natural sorting.
Technical Specification Snapshot
| Parameter | Description |
|---|---|
| Language | Python |
| Image processing library | Pillow |
| Sorting strategy | Regex-based natural sorting |
| Input formats | JPG, JPEG, PNG, BMP, WEBP, TIFF |
| Output strategy | Inherits the first image format and can preserve DPI |
| Layout capabilities | Grid layout, grouped output, centered last row |
| Quality control | LANCZOS resampling, JPG quality 100 |
| Stars | Not provided in the original article |
| License | Not provided in the original article |
| Core dependencies | Pillow, os, re |
This script delivers stable output for batch image collage workflows
Many online collage tools work well for one-off tasks, but once you move into research figures, presentation visuals, or asset archiving, three problems become obvious: weak batch-processing support, confusing filename ordering, and visually unbalanced final rows.
The real value of this script is that it turns “making a collage” into a repeatable engineering workflow. It uses the first image as the baseline for size, mode, and output parameters, then completes grouping, layout, and saving based on configuration. That makes it well suited for local automation.
The core configuration block defines collage behavior
from PIL import Image
import os
import re
# ==================== Manual configuration ====================
TARGET_PATH = r"your image folder path" # Image directory
IMAGES_PER_GROUP = 3 # Number of images to process per group
ROWS = 2 # Number of rows
COLS = 2 # Number of columns
BOTTOM_PADDING = 0 # Bottom padding in pixels
# ===================================================
This configuration block defines the input path, group size, and final canvas layout.
The natural sorting mechanism prevents incorrect orders like 1, 10, 2
Default string sorting compares characters one by one, so 10.jpg is placed before 2.jpg. That behavior is highly undesirable for numbered experiment figures, screenshot sequences, and result images.
Natural sorting works by splitting numeric and text segments in a filename with a regular expression, then converting numeric segments into integers for comparison. The resulting order matches how humans expect numbered files to appear.
The natural sort function is the foundation of correct file ordering
def natural_sort_key(s):
"""Natural sorting: ensure 1.jpg, 2.jpg, 10.jpg are ordered numerically"""
return [
int(text) if text.isdigit() else text.lower() # Convert numeric parts to integers and text parts to lowercase
for text in re.split(r'([0-9]+)', s)
]
image_files = sorted(raw_files, key=natural_sort_key) # Sort in natural order
This code ensures that batch-loaded images follow human-readable order.
Automatically centering the last row significantly improves visual balance
Many collage scripts place an incomplete final row directly against the left edge, which leaves a large blank area on the right. For academic figures or presentation documents, that makes the output look rough and unfinished.
This script first splits images into rows based on the column count, then checks whether the current row is full. If the row is incomplete, it calculates an offset from the row width and canvas width so the entire row starts from a horizontally centered position.
Offset calculation is the key to centering the final row
rows = []
for i in range(0, len(group_images), COLS):
rows.append(group_images[i:i + COLS]) # Split into multiple rows by column count
for row_idx, row_images in enumerate(rows):
y = row_idx * img_height
num_in_row = len(row_images)
if num_in_row == COLS:
offset_x = 0 # Start from the left edge for a full row
else:
total_width = num_in_row * img_width
offset_x = (canvas_width - total_width) // 2 # Center horizontally for an incomplete row
This code solves the awkward appearance of incomplete final rows and stands out as the script’s most distinctive feature.
Unifying size and color mode keeps canvas composition stable
In real-world workflows, images often have inconsistent dimensions and may mix modes such as RGB and RGBA. If you paste them directly, you may get misalignment, errors, or unexpected background behavior.
The script uses the first image as the reference. If later images do not match the target size, it resizes them with high-quality resampling. If their mode differs, it converts the mode first. This strategy significantly reduces batch-processing failures.
Image normalization ensures consistent output
with Image.open(img_path) as img:
if img.size != (img_width, img_height):
print(f"Warning: {os.path.basename(img_path)} has a different size and has been adjusted")
img = img.resize((img_width, img_height), Image.Resampling.LANCZOS) # High-quality scaling
if img.mode != img_mode:
img = img.convert(img_mode) # Normalize color mode
canvas.paste(img, (x, y)) # Paste into the target position
This code normalizes heterogeneous images into a consistent input format before reliable composition.
The high-quality save strategy works well for print-ready papers and archived reports
After the collage is complete, save parameters directly affect the final appearance. If you keep the default JPG compression settings, fine details are often lost. If the source image includes DPI metadata but you do not preserve it, printed dimensions may also become inaccurate.
For JPG and JPEG output, the script enables quality=100 and subsampling=0 to minimize compression loss. If the input image contains DPI metadata, the script keeps that original DPI so physical dimensions remain stable in print and layout workflows.
Save parameters control final image quality
save_params = {}
if input_extension.lower() in ['.jpg', '.jpeg']:
save_params['quality'] = 100 # Highest-quality JPG output
save_params['subsampling'] = 0 # Disable chroma subsampling
if img_dpi:
save_params['dpi'] = img_dpi # Preserve original DPI
canvas.save(save_path, **save_params)
This code preserves image clarity and print-related properties as much as possible during export.
The execution flow is straightforward and works well for local automation
Before running the script, you only need to install Pillow and change TARGET_PATH to your image directory. The script automatically filters common image formats and skips previously generated collage_final files to avoid duplicate processing.
If the image count exceeds the per-group limit, the script automatically splits the input into multiple collage outputs named collage_final_1, collage_final_2, and so on. This makes it suitable for one-time processing of large image collections.
Installation and execution are intentionally simple
pip install pillow
python image_collage.py
These two commands install the dependency and run the script.
This approach works well as an extensible foundation for batch image collage automation
From an engineering perspective, this script does not aim to provide a complex UI. Instead, it focuses on four high-value capabilities: correct ordering, natural layout, stable compatibility, and controllable output quality. For developers, those qualities matter more than simply being able to “make a collage.”
If you want to extend it further, you can add gap control, background color configuration, text watermarks, automatic EXIF orientation handling, or package it as a CLI tool or desktop application. Even in its current form, it is already sufficient for most research, reporting, and content production scenarios.

AI Visual Insight: This animated image shows a WeChat sharing prompt on a blog page. It illustrates platform interaction guidance rather than a technical architecture diagram, and it does not include script execution flow, layout algorithm details, or image processing results.
FAQ
1. Why does the script use the first image as the baseline?
Because the first image provides a consistent baseline for dimensions, color mode, output format, and DPI. That gives every subsequent image a clear normalization target before composition and reduces misalignment and mode-compatibility issues.
2. Why use Image.Resampling.LANCZOS?
It is one of Pillow’s high-quality scaling algorithms and is well suited for image resizing. If you are using an older Pillow version, you can replace it with Image.LANCZOS for compatibility.
3. Where should I modify the code if I want spacing between images?
You can add horizontal and vertical spacing when calculating canvas_width and canvas_height, then include a gap value in the x and y coordinates. The overall structure does not need to be rewritten; you only need to extend the layout formula.
AI Readability Summary
This article refactors a Python image collage script built with Pillow and explains the core implementation behind natural sorting, grid layout, automatic centering of the final row, size and color mode normalization, DPI preservation, and high-quality JPG export. It is especially useful for paper layout, report graphics, and batch asset-processing workflows.