74 lines
3.6 KiB
Python
74 lines
3.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Build a clean PTY-001 master and restore critical approved HUD geometry."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import cv2
|
|
import numpy as np
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
PAGE = ROOT / "docs/design-authority/pages/PTY-001-party-line"
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--approved", type=Path, default=PAGE / "approved/v001/master.png")
|
|
parser.add_argument("--generated", type=Path, default=PAGE / "reconstruction/v001/static-master/clean-static-master.png")
|
|
parser.add_argument("--regions", type=Path, default=PAGE / "reconstruction/v001/static-master/dynamic-regions.json")
|
|
parser.add_argument("--output", type=Path, default=PAGE / "reconstruction/v001/static-master/party-line-static-master.png")
|
|
parser.add_argument("--review", type=Path, default=PAGE / "reconstruction/v001/static-master/static-master-review.png")
|
|
args = parser.parse_args()
|
|
|
|
authority = json.loads(args.regions.read_text(encoding="utf-8"))
|
|
expected = tuple(authority["design_size"])
|
|
approved = cv2.imread(str(args.approved), cv2.IMREAD_COLOR)
|
|
generated = cv2.imread(str(args.generated), cv2.IMREAD_COLOR)
|
|
if approved is None or generated is None:
|
|
raise SystemExit("approved or generated master could not be read")
|
|
if (approved.shape[1], approved.shape[0]) != expected or (generated.shape[1], generated.shape[0]) != expected:
|
|
raise SystemExit("master dimensions do not match dynamic-regions authority")
|
|
|
|
# Use the clean edit as the complete painted base. It reconstructs the yard
|
|
# behind actors and the cord coherently, avoiding rectangular patch seams.
|
|
result = generated.copy()
|
|
# Restore critical approved geometry that the cleanup edit simplified:
|
|
# the complete top status rail and the fixed relation clipboard icon.
|
|
result[0:142, 0:2048] = approved[0:142, 0:2048]
|
|
result[169:287, 1470:1588] = approved[169:287, 1470:1588]
|
|
|
|
# Pressure dots are state values. Inpaint only their circular interiors so
|
|
# the exact approved weight icon and outlined status slot remain untouched.
|
|
pressure_mask = np.zeros(approved.shape[:2], dtype=np.uint8)
|
|
for region in authority["regions"]:
|
|
if region.get("treatment") != "approved_inpaint":
|
|
continue
|
|
for x, y, radius in region.get("circles", []):
|
|
cv2.circle(pressure_mask, (x, y), radius + 2, 255, thickness=-1)
|
|
result = cv2.inpaint(result, pressure_mask, 5, cv2.INPAINT_TELEA)
|
|
|
|
args.output.parent.mkdir(parents=True, exist_ok=True)
|
|
cv2.imwrite(str(args.output), result, [cv2.IMWRITE_PNG_COMPRESSION, 7])
|
|
|
|
approved_preview = Image.open(args.approved).convert("RGB").resize((1024, 576), Image.Resampling.LANCZOS)
|
|
clean_preview = Image.open(args.output).convert("RGB").resize((1024, 576), Image.Resampling.LANCZOS)
|
|
review = Image.new("RGB", (2048, 636), "#17252c")
|
|
review.paste(approved_preview, (0, 60))
|
|
review.paste(clean_preview, (1024, 60))
|
|
draw = ImageDraw.Draw(review)
|
|
font = ImageFont.truetype("/System/Library/Fonts/Supplemental/Arial.ttf", 24)
|
|
draw.text((28, 17), "APPROVED COMPOSITION (dynamic state baked in)", fill="#f3d991", font=font)
|
|
draw.text((1052, 17), "CLEAN STATIC MASTER (runtime overlay ready)", fill="#9ed7aa", font=font)
|
|
args.review.parent.mkdir(parents=True, exist_ok=True)
|
|
review.save(args.review, optimize=True)
|
|
print(f"ABG_PTY001_STATIC_MASTER_PREPARED output={args.output} review={args.review}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|