"""Tiles frames into a contact sheet: rows are views, columns are ticks, cropped to the player.""" import sys from pathlib import Path import numpy as np from PIL import Image name, views, ticks = sys.argv[1], sys.argv[2].split(","), range(*map(int, sys.argv[3].split(":"))) out = sys.argv[4] if len(sys.argv) > 4 else "sheet.png" frames = [[Image.open(Path("frames") / name / v / f"{t:04d}.png") for t in ticks] for v in views] alpha = np.maximum.reduce([np.asarray(f)[:, :, 3] for row in frames for f in row]) ys, xs = np.nonzero(alpha) box = (xs.min() - 4, ys.min() - 4, xs.max() + 4, ys.max() + 4) w, h = box[2] - box[0], box[3] - box[1] sheet = Image.new("RGBA", (w * len(ticks), h * len(views)), (40, 40, 52, 255)) for r, row in enumerate(frames): for c, f in enumerate(row): sheet.alpha_composite(f.crop(box), (c * w, r * h)) sheet.save(out) print(sheet.size)