build_template_contact_sheets.py 2.3 KB

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