Skip to content

Instantly share code, notes, and snippets.

@markusfisch
Last active January 2, 2022 13:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save markusfisch/a80ac0baadd79e51711b9028cf4fd9a1 to your computer and use it in GitHub Desktop.
Save markusfisch/a80ac0baadd79e51711b9028cf4fd9a1 to your computer and use it in GitHub Desktop.
Make a collage in shape of 2019 of your best shots of the year

Year In Pictures

Take your best picture of this year, put them into a folder and run this script:

$ python3 yip.py PICTURE_FOLDER

You'll get a collage in shape of 2019 where the digits are made from your pictures:

2019-preview

Requirements

You'll need to have Pillow:

$ pip3 install Pillow

Fix EXIF Orientation

Digital cameras often store pictures in camera orientation and just save a possibly deviating orientation with it. When the picture is displayed, it is rotated accordingly (see EXIF for details).

Pillow doesn't evaluate that EXIF orientation out of the box but provides an additional function for this: ImageOps.exif_transpose(). Unfortunately, this function failed quite often for me.

So, to fix this, I used Image Magick's mogrify to rotate the pictures into their intended orientation:

$ mogrify -auto-orient IMAGE_FILES...
#!/usr/bin/env python3
from PIL import Image, ImageOps
from random import shuffle
import os
import sys
def year_in_pictures(template, srcdir='.', outfile='yip.jpg'):
images = []
for f in os.listdir(srcdir):
if f.endswith('.jpg'):
images.append(os.path.join(srcdir, f))
if len(images) < 1:
sys.stderr.write('error: no JPG images found in %s\n' % (srcdir, ))
exit()
lines = template.split('\n')
width = len(max(lines))
height = len(lines)
cellSize = 320
width *= cellSize
height *= cellSize
canvas = Image.new('RGB', (width, height))
shuffle(images)
i = 0
y = 0
x = 0
for line in lines:
for ch in line:
if ch == 'X':
im = ImageOps.exif_transpose(Image.open(
images[i % len(images)]
))
i += 1
(w, h) = im.size
scale = max(cellSize / w, cellSize / h)
w = round(w * scale)
h = round(h * scale)
im = im.resize((w, h), resample=Image.BICUBIC).crop(
(0, 0, cellSize, cellSize)
)
canvas.paste(im, (x, y))
x += cellSize
x = 0
y += cellSize
canvas.save(outfile, quality=95)
template = """
.XXX.XXX.XXX...X.
...X.X.X...X..XX.
.XXX.X.X.XXX...X.
.X...X.X.X.....X.
.XXX.XXX.XXX...X.
"""
year_in_pictures(template, * sys.argv[1:])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment