Simam3D-GPU / evaluate_reprojection.py
junaid-simamdigital's picture
Add independent reprojection evaluator
8cae901 verified
Raw History Blame Contribute Delete
2.71 kB
"""Evaluate point-cloud reprojection consistency against exported depth maps."""
from __future__ import annotations
import argparse
import json
from pathlib import Path
import numpy as np
from evaluate_pointcloud import load_points
from simam3d_core import reprojection_consistency
def evaluate_reprojection(points_path: str | Path, metadata_path: str | Path) -> dict:
points = load_points(points_path)
with np.load(metadata_path) as metadata:
required = {"depth", "intrinsics", "extrinsics"}
missing = sorted(required.difference(metadata.files))
if missing:
raise ValueError(f"metadata is missing required arrays: {', '.join(missing)}")
depth = np.asarray(metadata["depth"])
intrinsics = np.asarray(metadata["intrinsics"])
extrinsics = np.asarray(metadata["extrinsics"])
if depth.ndim != 3:
raise ValueError("depth metadata must have shape VxHxW")
view_count = depth.shape[0]
if intrinsics.shape != (view_count, 3, 3) or extrinsics.shape != (view_count, 4, 4):
raise ValueError("camera metadata must match depth view count")
views = []
for index in range(view_count):
metrics = reprojection_consistency(points, depth[index], intrinsics[index], extrinsics[index])
views.append({"index": index, **metrics})
return {
"mode": "reprojection_consistency_evaluation",
"points": str(Path(points_path)),
"metadata": str(Path(metadata_path)),
"point_count": int(len(points)),
"view_count": view_count,
"mean_coverage": float(np.mean([view["coverage"] for view in views])),
"mean_absolute_error": float(np.mean([view["mean_absolute_error"] for view in views])),
"median_absolute_error": float(np.median([view["median_absolute_error"] for view in views])),
"interpretation": "cross-view consistency diagnostic; not a geometric accuracy or hidden-surface claim",
"views": views,
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("points", type=Path)
parser.add_argument("metadata", type=Path, help="run NPZ containing depth, intrinsics, and extrinsics")
parser.add_argument("--output", type=Path)
args = parser.parse_args(argv)
report = evaluate_reprojection(args.points, args.metadata)
destination = args.output
if destination:
destination.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")
print(json.dumps(report, indent=2))
if destination:
print(f"reprojection report: {destination}")
return 0
if __name__ == "__main__":
raise SystemExit(main())