#!/usr/bin/env python3 """Write an auditable preflight and multiview verification report for one run.""" from __future__ import annotations import argparse import json import sys from pathlib import Path import numpy as np from PIL import Image PROJECT_ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(PROJECT_ROOT)) from accessibilityamodal.verification import build_verification # noqa: E402 def read_json(path: Path) -> dict: return json.loads(path.read_text(encoding="utf-8")) def read_mask(path: Path) -> np.ndarray: mask = np.asarray(Image.open(path).convert("L")) > 127 return mask def find_sam_quality(run_dir: Path, sample_id: str) -> dict | None: for root in (run_dir / "01_sam3", run_dir / "01_sam3_retry"): path = root / "samples" / sample_id / "quality.json" if path.is_file(): return read_json(path) return None def find_reviewed_visible_metadata(run_dir: Path) -> dict | None: path = run_dir / "01_reviewed_visible" / "metadata.json" return read_json(path) if path.is_file() else None def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--run-dir", required=True) parser.add_argument("--sample-id", required=True) parser.add_argument("--category", required=True, choices=["curb_cut", "ramp", "stairs", "tactile_paving", "walkway"]) parser.add_argument("--output", default=None) parser.add_argument("--preflight-only", action="store_true") parser.add_argument("--print-decision", action="store_true") args = parser.parse_args() run_dir = Path(args.run_dir).expanduser().resolve() masks_dir = run_dir / "02_masks" visible = read_mask(masks_dir / "target_visible.png") amodal = read_mask(masks_dir / "target_amodal.png") hidden = read_mask(masks_dir / "hidden.png") obstacle = read_mask(masks_dir / "obstacle.png") geometry_path = run_dir / "04_3d_completion" / "geometry" / "geometry_manifest.json" geometry = None if args.preflight_only or not geometry_path.is_file() else read_json(geometry_path) learned_dir = None if not args.preflight_only: learned_candidates = ( run_dir / "accessibility3d", run_dir / "04_3d_completion" / "visual_candidate", ) learned_dir = next( (candidate for candidate in learned_candidates if candidate.is_dir()), learned_candidates[0], ) report = build_verification( sample_id=args.sample_id, category=args.category, visible_mask=visible, amodal_mask=amodal, hidden_mask=hidden, obstacle_mask=obstacle, sam3_quality=find_sam_quality(run_dir, args.sample_id), reviewed_visible_metadata=find_reviewed_visible_metadata(run_dir), geometry_manifest=geometry, learned_dir=learned_dir, ) output = ( Path(args.output).expanduser().resolve() if args.output else run_dir / "05_quality_checks" / "verification.json" ) output.parent.mkdir(parents=True, exist_ok=True) output.write_text(json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True) + "\n", encoding="utf-8") if args.print_decision: print(report["decision"]) else: print(json.dumps({"output": str(output), "decision": report["decision"], "gate": report["gate"]}, ensure_ascii=False)) return 0 if __name__ == "__main__": raise SystemExit(main())