feat: /api/intrinsics gives what an align outside the SDK needs
Already there: fx/fy/cx/cy and k1,k2,k3,p1,p2 of both streams, extrinsics depth->color (rotation row-major, translation mm). Added, as new keys only: width, height and distortion model name (an rs.distortion member) in "ir" and "rgb", and depth_scale_m at the top. Docstring states the extrinsics convention: p_rgb = R p_ir + t, R row-major (the SDK's is column-major). Same keys in /api/status and /api/open, which share the payload. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e11565de04
commit
f58b81cb73
2 files changed
+29
-1
No files matched your search
@@ -269,7 +269,13 @@ def _frame_to_dict(frame: CameraFrame, include_images: bool = True) -> dict:
|
|||||||
|
|
||||||
|
|
||||||
def _calibration_payload() -> dict | None:
|
def _calibration_payload() -> dict | None:
|
||||||
"""Full calibration (intrinsics + distortion + extrinsics) or None."""
|
"""Full calibration (intrinsics + distortion + extrinsics) or None.
|
||||||
|
|
||||||
|
Enough to align depth to color outside the SDK: per stream width, height
|
||||||
|
and distortion model (rs.distortion member name), depth_scale_m (meters per
|
||||||
|
raw unit). extrinsics_ir_to_rgb: p_rgb = R p_ir + t, R row-major (the SDK's
|
||||||
|
is column-major), t in millimeters.
|
||||||
|
"""
|
||||||
if not _camera or not _camera.intrinsics:
|
if not _camera or not _camera.intrinsics:
|
||||||
return None
|
return None
|
||||||
i = _camera.intrinsics
|
i = _camera.intrinsics
|
||||||
@@ -280,19 +286,26 @@ def _calibration_payload() -> dict | None:
|
|||||||
def _d(d):
|
def _d(d):
|
||||||
return None if d is None else {"k1": d.k1, "k2": d.k2, "k3": d.k3, "p1": d.p1, "p2": d.p2}
|
return None if d is None else {"k1": d.k1, "k2": d.k2, "k3": d.k3, "p1": d.p1, "p2": d.p2}
|
||||||
|
|
||||||
|
def _stream(name):
|
||||||
|
width, height = _camera.stream_sizes.get(name, (None, None))
|
||||||
|
return {"width": width, "height": height, "model": _camera.distortion_models.get(name)}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"ir": {
|
"ir": {
|
||||||
"fx": i.fx_ir, "fy": i.fy_ir, "cx": i.cx_ir, "cy": i.cy_ir,
|
"fx": i.fx_ir, "fy": i.fy_ir, "cx": i.cx_ir, "cy": i.cy_ir,
|
||||||
"distortion": _d(dir_),
|
"distortion": _d(dir_),
|
||||||
|
**_stream("ir"),
|
||||||
},
|
},
|
||||||
"rgb": {
|
"rgb": {
|
||||||
"fx": i.fx_rgb, "fy": i.fy_rgb, "cx": i.cx_rgb, "cy": i.cy_rgb,
|
"fx": i.fx_rgb, "fy": i.fy_rgb, "cx": i.cx_rgb, "cy": i.cy_rgb,
|
||||||
"distortion": _d(drgb),
|
"distortion": _d(drgb),
|
||||||
|
**_stream("rgb"),
|
||||||
},
|
},
|
||||||
"extrinsics_ir_to_rgb": None if e is None else {
|
"extrinsics_ir_to_rgb": None if e is None else {
|
||||||
"rotation": list(e.rotation),
|
"rotation": list(e.rotation),
|
||||||
"translation": list(e.translation),
|
"translation": list(e.translation),
|
||||||
},
|
},
|
||||||
|
"depth_scale_m": _camera.depth_scale(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -79,6 +79,11 @@ class RawFrame:
|
|||||||
depth: np.ndarray # (H, W) uint16 millimeters, depth sensor grid, own memory
|
depth: np.ndarray # (H, W) uint16 millimeters, depth sensor grid, own memory
|
||||||
|
|
||||||
|
|
||||||
|
def _model_name(model) -> str:
|
||||||
|
"""'distortion.inverse_brown_conrady' -> 'inverse_brown_conrady' (a member of rs.distortion)."""
|
||||||
|
return str(model).split(".")[-1]
|
||||||
|
|
||||||
|
|
||||||
def _domain_name(domain) -> str:
|
def _domain_name(domain) -> str:
|
||||||
"""'timestamp_domain.hardware_clock' -> 'hardware_clock'."""
|
"""'timestamp_domain.hardware_clock' -> 'hardware_clock'."""
|
||||||
return str(domain).split(".")[-1]
|
return str(domain).split(".")[-1]
|
||||||
@@ -173,6 +178,10 @@ class RealSenseD435:
|
|||||||
self.extrinsics: Optional[CameraExtrinsics] = None
|
self.extrinsics: Optional[CameraExtrinsics] = None
|
||||||
self.distortion_ir: Optional[Distortion] = None
|
self.distortion_ir: Optional[Distortion] = None
|
||||||
self.distortion_rgb: Optional[Distortion] = None
|
self.distortion_rgb: Optional[Distortion] = None
|
||||||
|
# Per stream ("ir" = depth, "rgb" = color): image size and distortion model name,
|
||||||
|
# what an align outside the SDK needs besides the intrinsics above.
|
||||||
|
self.stream_sizes: dict[str, tuple[int, int]] = {}
|
||||||
|
self.distortion_models: dict[str, str] = {}
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# Lifecycle
|
# Lifecycle
|
||||||
@@ -446,6 +455,12 @@ class RealSenseD435:
|
|||||||
|
|
||||||
self.distortion_ir = _to_distortion(list(di.coeffs))
|
self.distortion_ir = _to_distortion(list(di.coeffs))
|
||||||
self.distortion_rgb = _to_distortion(list(ci.coeffs))
|
self.distortion_rgb = _to_distortion(list(ci.coeffs))
|
||||||
|
self.stream_sizes = {"ir": (di.width, di.height), "rgb": (ci.width, ci.height)}
|
||||||
|
self.distortion_models = {"ir": _model_name(di.model), "rgb": _model_name(ci.model)}
|
||||||
|
|
||||||
|
def depth_scale(self) -> float:
|
||||||
|
"""Meters per raw z16 depth unit."""
|
||||||
|
return self._depth_scale
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# Frame capture
|
# Frame capture
|
||||||
|
|||||||
Reference in new issue
Block a user