#!/usr/bin/env python3 """Run scared_dataset_benchmark over many keyframes and print a Markdown table. Usage: scripts/scared_overview.py [--bench PATH] [--disparities N] [--png-dir DIR] [--json-dir DIR] [--depth-range MIN_M MAX_M] KEYFRAME_DIR... Each KEYFRAME_DIR must hold Left_Image.png, Right_Image.png and endoscope_calibration.yaml. Accuracy columns are filled in only for keyframes that also contain point_cloud.obj. """ import argparse import json import os import subprocess import sys def run(bench, kf, disparities, png_dir, json_dir, depth_range): label = "/".join(kf.rstrip("/").split("/")[-2:]) cmd = [bench, kf, str(disparities)] if png_dir: os.makedirs(png_dir, exist_ok=True) cmd.append(os.path.join(png_dir, label.replace("/", "_") + ".png")) else: cmd.append("-") if depth_range: cmd += [str(depth_range[0]), str(depth_range[1])] proc = subprocess.run(cmd, capture_output=True, text=True) if proc.returncode != 0: print(f"{label}: benchmark failed\n{proc.stderr}", file=sys.stderr) return label, None result = json.loads(proc.stdout) if json_dir: os.makedirs(json_dir, exist_ok=True) with open(os.path.join(json_dir, label.replace("/", "_") + ".json"), "w") as f: json.dump(result, f, indent=2) return label, result def fmt_mm(v): return "" if v is None else f"{v * 1000:.2f}" def fmt_pct(v): return "" if v is None else f"{v * 100:.1f}" def main(): ap = argparse.ArgumentParser() ap.add_argument("--bench", default="build-opencv/src/cloud_point/scared_dataset_benchmark") ap.add_argument("--disparities", type=int, default=160) ap.add_argument("--png-dir") ap.add_argument("--json-dir") ap.add_argument("--depth-range", nargs=2, type=float, metavar=("MIN_M", "MAX_M"), help="depth filter in metres (default: builder defaults 0.01..10)") ap.add_argument("keyframes", nargs="+") args = ap.parse_args() rows = [run(args.bench, kf, args.disparities, args.png_dir, args.json_dir, args.depth_range) for kf in args.keyframes] print("| keyframe | valid % | z p5 / median / p95 (mm) | match ms | GT | coverage % | MAE3D mm | RMSE3D mm | median mm | <1 mm % | <2 mm % | <5 mm % |") print("|---|---|---|---|---|---|---|---|---|---|---|---|") for label, r in rows: if r is None: print(f"| {label} | failed | | | | | | | | | | |") continue z = f"{r['z_p05_m']*1000:.0f} / {r['z_median_m']*1000:.0f} / {r['z_p95_m']*1000:.0f}" gt = r.get("has_ground_truth", False) print("| {} | {} | {} | {:.0f} | {} | {} | {} | {} | {} | {} | {} | {} |".format( label, fmt_pct(r["valid_fraction"]), z, r["matching_ms"], "yes" if gt else "no", fmt_pct(r.get("coverage")), fmt_mm(r.get("mae_3d_m")), fmt_mm(r.get("rmse_3d_m")), fmt_mm(r.get("median_3d_m")), fmt_pct(r.get("within_1mm")), fmt_pct(r.get("within_2mm")), fmt_pct(r.get("within_5mm")))) if __name__ == "__main__": main()