build_music_candidate_contact_sheets.py 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. from __future__ import annotations
  2. import json
  3. from pathlib import Path
  4. from PIL import Image, ImageDraw, ImageFont
  5. ROOT = Path(__file__).resolve().parents[1]
  6. REVIEW_DIR = ROOT / "artifacts" / "music-template-extraction" / "review"
  7. OUTPUT_DIR = ROOT / "artifacts" / "music-template-extraction" / "contact-sheets-local"
  8. def load_font(size: int) -> ImageFont.FreeTypeFont | ImageFont.ImageFont:
  9. for path in (
  10. Path(r"C:\Windows\Fonts\msyh.ttc"),
  11. Path(r"C:\Windows\Fonts\simhei.ttf"),
  12. ):
  13. if path.exists():
  14. return ImageFont.truetype(str(path), size)
  15. return ImageFont.load_default()
  16. def crop_text(text: str, length: int = 38) -> str:
  17. return text if len(text) <= length else f"{text[:length]}…"
  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. title_font = load_font(24)
  23. meta_font = load_font(17)
  24. for scene in dict.fromkeys(item["scene"] for item in items):
  25. scene_items = [item for item in items if item["scene"] == scene]
  26. width = 1820
  27. row_height = 245
  28. canvas = Image.new("RGB", (width, 52 + row_height * len(scene_items)), "#f5f7fa")
  29. draw = ImageDraw.Draw(canvas)
  30. draw.text((18, 12), f"{scene} · {len(scene_items)} candidates", fill="#172033", font=title_font)
  31. for row, item in enumerate(scene_items):
  32. top = 52 + row * row_height
  33. draw.rectangle((8, top + 4, width - 8, top + row_height - 4), fill="white", outline="#d8dee8")
  34. label = f"{item['id']} {crop_text(item['name'])} · {item['slides']}页 · {item['score']}分"
  35. draw.text((20, top + 14), label, fill="#172033", font=meta_font)
  36. for column, filename in enumerate(item["samples"]):
  37. image_path = REVIEW_DIR / item["id"] / filename
  38. with Image.open(image_path) as sample:
  39. sample = sample.convert("RGB")
  40. sample.thumbnail((342, 192))
  41. x = 20 + column * 358
  42. y = top + 45
  43. canvas.paste(sample, (x, y))
  44. draw.rectangle((x, y, x + sample.width, y + sample.height), outline="#c9d1dc")
  45. canvas.save(OUTPUT_DIR / f"{scene}.jpg", quality=91, optimize=True)
  46. if __name__ == "__main__":
  47. main()