The depth-map file scalar; a loaded Depth is the file path.
Reader methods (depth.to_array(), depth.point_cloud(pose)) read
self via OpenEXR. A depth map is a non-core extension scalar (dotted
reverse-DNS name), so its
schema self-describes with kind + extensions and a reader without an EXR
decoder can still store or skip the bytes. The format is single-file 32-bit float
OpenEXR: the R channel is metric depth in metres, and an optional A
channel is per-pixel confidence in [0, 1].
numpy/OpenEXR are lazy-imported so importing the type system stays cheap.
Depth
Bases: FileScalar
A per-pixel metric depth map as 32-bit float OpenEXR.
A loaded value is its file Path plus the methods below, which read
self via OpenEXR. The R channel holds depth in metres; an optional
A channel holds per-pixel confidence in [0, 1].
Source code in capturegraph-lib/capturegraph/types/scalars/geometry/depth.py
| class Depth(FileScalar, name="edu.cornell.depth", extensions=("exr",)):
"""A per-pixel metric depth map as 32-bit float OpenEXR.
A loaded value is its file ``Path`` plus the methods below, which read
``self`` via OpenEXR. The ``R`` channel holds depth in metres; an optional
``A`` channel holds per-pixel confidence in ``[0, 1]``.
"""
__slots__ = ()
def to_array(self) -> "np.ndarray":
"""The depth map as an ``H×W`` float array of metres.
Raises:
OSError: If the EXR carries no ``R`` (depth) channel.
"""
channels = _read_channels(str(self))
if "R" not in channels:
raise OSError(f"Depth EXR has no R channel: {self}")
return channels["R"]
def confidence(self) -> "np.ndarray | None":
"""The per-pixel confidence as an ``H×W`` float array in ``[0, 1]``.
``None`` when the depth map carries no confidence channel.
"""
return _read_channels(str(self)).get("A")
def point_cloud(self, pose: "Pose") -> "np.ndarray":
"""Back-project the depth map into an ``N×3`` array of camera-space points.
Points are in metres, using the pinhole intrinsics carried by ``pose``.
Raises:
ValueError: If ``pose`` carries no intrinsics
(``focal_x``/``focal_y``/``center_x``/``center_y``).
"""
import numpy as np
if None in (pose.focal_x, pose.focal_y, pose.center_x, pose.center_y):
raise ValueError(
"point_cloud requires pose intrinsics (focal_x, focal_y, center_x, center_y)"
)
depth = self.to_array()
height, width = depth.shape[:2]
scale_x = width / (pose.reference_width or width)
scale_y = height / (pose.reference_height or height)
focal_x = pose.focal_x * scale_x # type: ignore[operator] # guarded above
focal_y = pose.focal_y * scale_y # type: ignore[operator]
center_x = pose.center_x * scale_x # type: ignore[operator]
center_y = pose.center_y * scale_y # type: ignore[operator]
rows, cols = np.indices((height, width), dtype=np.float32)
z = depth.astype(np.float32)
x = (cols - center_x) * z / focal_x
y = (rows - center_y) * z / focal_y
points = np.stack((x, y, z), axis=-1).reshape(-1, 3)
return points[np.isfinite(points).all(axis=1) & (points[:, 2] > 0)]
|
confidence()
The per-pixel confidence as an H×W float array in [0, 1].
None when the depth map carries no confidence channel.
Source code in capturegraph-lib/capturegraph/types/scalars/geometry/depth.py
| def confidence(self) -> "np.ndarray | None":
"""The per-pixel confidence as an ``H×W`` float array in ``[0, 1]``.
``None`` when the depth map carries no confidence channel.
"""
return _read_channels(str(self)).get("A")
|
point_cloud(pose)
Back-project the depth map into an N×3 array of camera-space points.
Points are in metres, using the pinhole intrinsics carried by pose.
Raises:
| Type |
Description |
ValueError
|
If pose carries no intrinsics
(focal_x/focal_y/center_x/center_y).
|
Source code in capturegraph-lib/capturegraph/types/scalars/geometry/depth.py
| def point_cloud(self, pose: "Pose") -> "np.ndarray":
"""Back-project the depth map into an ``N×3`` array of camera-space points.
Points are in metres, using the pinhole intrinsics carried by ``pose``.
Raises:
ValueError: If ``pose`` carries no intrinsics
(``focal_x``/``focal_y``/``center_x``/``center_y``).
"""
import numpy as np
if None in (pose.focal_x, pose.focal_y, pose.center_x, pose.center_y):
raise ValueError(
"point_cloud requires pose intrinsics (focal_x, focal_y, center_x, center_y)"
)
depth = self.to_array()
height, width = depth.shape[:2]
scale_x = width / (pose.reference_width or width)
scale_y = height / (pose.reference_height or height)
focal_x = pose.focal_x * scale_x # type: ignore[operator] # guarded above
focal_y = pose.focal_y * scale_y # type: ignore[operator]
center_x = pose.center_x * scale_x # type: ignore[operator]
center_y = pose.center_y * scale_y # type: ignore[operator]
rows, cols = np.indices((height, width), dtype=np.float32)
z = depth.astype(np.float32)
x = (cols - center_x) * z / focal_x
y = (rows - center_y) * z / focal_y
points = np.stack((x, y, z), axis=-1).reshape(-1, 3)
return points[np.isfinite(points).all(axis=1) & (points[:, 2] > 0)]
|
to_array()
The depth map as an H×W float array of metres.
Raises:
| Type |
Description |
OSError
|
If the EXR carries no R (depth) channel.
|
Source code in capturegraph-lib/capturegraph/types/scalars/geometry/depth.py
| def to_array(self) -> "np.ndarray":
"""The depth map as an ``H×W`` float array of metres.
Raises:
OSError: If the EXR carries no ``R`` (depth) channel.
"""
channels = _read_channels(str(self))
if "R" not in channels:
raise OSError(f"Depth EXR has no R channel: {self}")
return channels["R"]
|