picStitcher – creating a photo collage

Every month since our son was born, my wife and I have made a 4×4 collage of the best photos of him which goes on the fridge.

We’ve then taken a photo of the photos, but the quality is obviously not as good as if we’d joined the originals together. Rather than rely on photo editing software, or an online tool, I thought I’d see how easy it is to make a tool for this in Python.

The problem

16 photos per month, saved in jpg format, need to be stitched together in a single jpg

A schematic showing 16 individual images, then those images in a grid, finally those files joined as 1

The photos are all portrait, and I want to preserve the original order from the fridge

The solution

I think the steps are:

  1. Rename the files into some kind of sequence (I wonder if I could enhance this with some image recognition eventually?)
  2. Create a new blank image to hold them
  3. Loop through the files and add them to the blank image at the right offset
  4. Save the new file

1. Rename the files

This turned out to be the most tedious bit of the job. I went through a folder of all the fridge photo jpgs from the year and renamed them as

MMM-Y-X.jpg

a list of properly formatted file names

where x and y are the grid co-ordinates, from 1 to 4. With that done, next stop VSCode

2. Create a blank image

I’ve used the Pillow library a few times, it’s pretty accommodating, and we’ll enough used that stackoverflow produces plenty of help when it breaks (or you do it wrong).

from PIL import Image, ImageDraw, ImageOps

The first thing to do is make a new image, which takes 2 arguments. The first is the colour space for the image, then the width and height in a tuple.

Image.new(mode, size, color=0)

rather than worrying about how many pixels each photo was, I thought it was easier to open the first photo for each grid and read in the width and height values.

#All the files are named MON-Y-X.jpg
month = "NOV"
gridX = 4
gridY = 4

#build the image name
imageName = month+"-1-1.jpg"
#open the file
firstImage = Image.open(imageName)
#check the orientation is correct
firstImage = ImageOps.exif_transpose(firstImage)
#get the size of the image
imageWidth, imageHeight = firstImage.size

then the new image is just the number of tiled images multiplied by the size of each one

#make a new image which is large enough to fit the sub-images in
newImage = Image.new('RGB', (imageWidth*gridX, imageHeight*gridY))

3. Stitch them all together

I’m still second guessing myself in Python when it comes to loops. I’ve read a lot of very stuffy views on the unsuitability of looping (“It’s just not Pythonic” stuff) but I think this is one of the correct uses (small volumes, speed not a factor, readability important)

#loop through, opening each image and pasting it into the new image
for x in range(gridX):
    for y in range(gridY):
        getImageName= month+"-"+str(y+1)+"-"+str(x+1)+".jpg"
        getImage = Image.open(getImageName)
        getImage=ImageOps.exif_transpose(getImage)
        #force a resize, just in case
        getImage = getImage.resize((imageWidth,imageHeight), Image.ANTIALIAS)
        
        #paste into the new image
        newImage.paste(getImage,(imageWidth*x,imageHeight*y))
        #quick debug print
        print(getImageName+ " done")

3b. Don’t make assumptions

I’ve put a couple of bits of code in there which weren’t in the original plan, but address issues I got to when I ran the code

firstImage = ImageOps.exif_transpose(firstImage)

This solved a couple of the pictures having the wrong orientation. I think the issue is that somewhere in the history of images and cameras there was a bifurcation of reason which lead to us having some image files orientated correctly, and others arbitrarily, but with a note to tell the software how to rotate it. Seems crazy to me.

getImage = getImage.resize((imageWidth,imageHeight), Image.ANTIALIAS)

This just ensures the image I’m going to tile isn’t an unusual size

4. Save the file

newImage = newImage.resize((6000,8000), Image.ANTIALIAS)

#Save the new image with the month name
newImage.save(month+".jpg")

I didn’t initially worry about the final image size, but google photos had a freak-out when I tried to upload a 25mb enormous jpg, so sorting the size before saving helped

The result

a (blurry) sample of the output

I’m not sure if it beats the time spent vs time saved heuristic, but I’m pleased with the result!

1 thought on “picStitcher – creating a photo collage

  1. Pingback: picStitcher2 – automatically creating a photo collage | Alex’s Blog

Leave a Reply

Your email address will not be published. Required fields are marked *