from __future__ import annotations import json import sys from pathlib import Path from PIL import Image, ImageDraw, ImageFont REVIEW_DIR = Path(sys.argv[1] if len(sys.argv) > 1 else "artifacts/music-template-extraction/review") OUTPUT_DIR = Path(sys.argv[2] if len(sys.argv) > 2 else "artifacts/music-template-extraction/contact-sheets") ROWS_PER_SHEET = 6 THUMBNAIL = (300, 169) MARGIN = 18 LABEL_HEIGHT = 54 def font(size: int, bold: bool = False): path = Path(r"C:\Windows\Fonts\msyhbd.ttc" if bold else r"C:\Windows\Fonts\msyh.ttc") try: return ImageFont.truetype(str(path), size) except OSError: return ImageFont.load_default() def main() -> None: manifest = json.loads((REVIEW_DIR / "manifest.json").read_text(encoding="utf-8")) items = manifest["items"] OUTPUT_DIR.mkdir(parents=True, exist_ok=True) width = MARGIN * 6 + THUMBNAIL[0] * 5 row_height = THUMBNAIL[1] + LABEL_HEIGHT + MARGIN for sheet_index, start in enumerate(range(0, len(items), ROWS_PER_SHEET), start=1): page_items = items[start:start + ROWS_PER_SHEET] canvas = Image.new("RGB", (width, row_height * len(page_items) + MARGIN), "#EEF1F5") draw = ImageDraw.Draw(canvas) for row, item in enumerate(page_items): top = MARGIN + row * row_height label = f"{item['id']} [{item['scene']}] {item['name']}" draw.text((MARGIN, top), label[:75], font=font(22, True), fill="#202631") for column, sample in enumerate(item["samples"][:5]): source = REVIEW_DIR / item["id"] / sample with Image.open(source) as raw: image = raw.convert("RGB") image.thumbnail(THUMBNAIL, Image.Resampling.LANCZOS) left = MARGIN + column * (THUMBNAIL[0] + MARGIN) image_top = top + LABEL_HEIGHT canvas.paste(image, (left, image_top)) draw.rectangle((left, image_top, left + THUMBNAIL[0], image_top + THUMBNAIL[1]), outline="#B7C0CD", width=2) canvas.save(OUTPUT_DIR / f"sheet-{sheet_index:02d}.jpg", quality=90, optimize=True) print(json.dumps({"sheets": (len(items) + ROWS_PER_SHEET - 1) // ROWS_PER_SHEET, "items": len(items)}, ensure_ascii=False)) if __name__ == "__main__": main()