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
The photos are all portrait, and I want to preserve the original order from the fridge
The solution
I think the steps are:
- Rename the files into some kind of sequence (I wonder if I could enhance this with some image recognition eventually?)
- Create a new blank image to hold them
- Loop through the files and add them to the blank image at the right offset
- 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
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
I’m not sure if it beats the time spent vs time saved heuristic, but I’m pleased with the result!
Pingback: picStitcher2 – automatically creating a photo collage | Alex’s Blog