feat(cli): add depth verify/refine outputs and tests

- Retrieve depth + confidence measures from SVOReader when depth enabled
- Compute depth residual metrics and attach to output JSON
- Optionally write per-corner residual CSV via --report-csv
- Post-process refinement: optimize final pose and report pre/post metrics
- Add unit tests for depth verification and refinement modules
- Add basedpyright dev dependency for diagnostics
This commit is contained in:
2026-02-05 04:44:34 +00:00
parent 882d9348a6
commit 6d3c5cc5c1
10 changed files with 514 additions and 76 deletions
+15 -11
View File
@@ -1,7 +1,7 @@
import pyzed.sl as sl
import numpy as np
from dataclasses import dataclass
from typing import List, Optional, Dict
from typing import Any
import os
@@ -13,18 +13,22 @@ class FrameData:
timestamp_ns: int
frame_index: int
serial_number: int
depth_map: Optional[np.ndarray] = None
depth_map: np.ndarray | None = None
class SVOReader:
"""Handles synchronized playback of multiple SVO files."""
svo_paths: list[str]
runtime_params: sl.RuntimeParameters
_depth_mode: sl.DEPTH_MODE
def __init__(
self, svo_paths: List[str], depth_mode: sl.DEPTH_MODE = sl.DEPTH_MODE.NONE
self, svo_paths: list[str], depth_mode: sl.DEPTH_MODE = sl.DEPTH_MODE.NONE
):
self.svo_paths = svo_paths
self.cameras: List[sl.Camera] = []
self.camera_info: List[Dict] = []
self.cameras: list[sl.Camera] = []
self.camera_info: list[dict[str, Any]] = []
self.runtime_params = sl.RuntimeParameters()
self._depth_mode = depth_mode
@@ -83,9 +87,9 @@ class SVOReader:
else:
cam.set_svo_position(0)
def grab_all(self) -> List[Optional[FrameData]]:
def grab_all(self) -> list[FrameData | None]:
"""Grabs a frame from all cameras without strict synchronization."""
frames = []
frames: list[FrameData | None] = []
for i, cam in enumerate(self.cameras):
err = cam.grab(self.runtime_params)
if err == sl.ERROR_CODE.SUCCESS:
@@ -110,7 +114,7 @@ class SVOReader:
frames.append(None)
return frames
def grab_synced(self, tolerance_ms: int = 33) -> List[Optional[FrameData]]:
def grab_synced(self, tolerance_ms: int = 33) -> list[FrameData | None]:
"""
Grabs frames from all cameras, attempting to keep them within tolerance_ms.
If a camera falls behind, it skips frames.
@@ -168,14 +172,14 @@ class SVOReader:
def enable_depth(self) -> bool:
return self._depth_mode != sl.DEPTH_MODE.NONE
def _retrieve_depth(self, cam: sl.Camera) -> Optional[np.ndarray]:
def _retrieve_depth(self, cam: sl.Camera) -> np.ndarray | None:
if not self.enable_depth:
return None
depth_mat = sl.Mat()
cam.retrieve_measure(depth_mat, sl.MEASURE.DEPTH)
return depth_mat.get_data().copy()
def get_depth_at(self, frame: FrameData, x: int, y: int) -> Optional[float]:
def get_depth_at(self, frame: FrameData, x: int, y: int) -> float | None:
if frame.depth_map is None:
return None
h, w = frame.depth_map.shape[:2]
@@ -188,7 +192,7 @@ class SVOReader:
def get_depth_window_median(
self, frame: FrameData, x: int, y: int, size: int = 5
) -> Optional[float]:
) -> float | None:
if frame.depth_map is None:
return None
if size % 2 == 0: