rpx_benchmark.metrics.depth_alignment
Per-frame alignment of a predicted depth map to GT scale.
Used by both:
- the runner's primary-metric path (
BatchedDepthBenchmarkModel.predictapplies the adapter'snative_alignmentbefore the runner sees the prediction, so result.json'saggregated.absrelis meaningful for relative-depth models like Marigold / MiDaS); and - the post-processor's metric basket (
comprehensive_depth_metrics) which iterates alignment modes per-frame.
Single source of truth for the alignment math so a fix in one path applies everywhere. Modes match the standard mono-depth-evaluation literature.
1"""Per-frame alignment of a predicted depth map to GT scale. 2 3Used by both: 4 5* the runner's primary-metric path (``BatchedDepthBenchmarkModel.predict`` 6 applies the adapter's ``native_alignment`` before the runner sees the 7 prediction, so result.json's ``aggregated.absrel`` is meaningful for 8 relative-depth models like Marigold / MiDaS); and 9* the post-processor's metric basket (``comprehensive_depth_metrics``) 10 which iterates alignment modes per-frame. 11 12Single source of truth for the alignment math so a fix in one path 13applies everywhere. Modes match the standard mono-depth-evaluation 14literature. 15""" 16 17from __future__ import annotations 18 19from typing import Optional 20 21import numpy as np 22 23from ..exceptions import ConfigError 24 25__all__ = [ 26 "DEPTH_MIN_M", 27 "DEPTH_MAX_M", 28 "ALIGNMENT_MODES", 29 "align_pred_to_gt", 30 "align_pred_to_gt_pooled", 31 "default_valid_mask", 32] 33 34 35#: D435 sensor working range. Pixels outside this range are excluded 36#: from the alignment fit (and from every metric — see 37#: ``comprehensive_depth_metrics._valid``). Kept here so any future 38#: change is one-place. 39DEPTH_MIN_M: float = 0.3 40DEPTH_MAX_M: float = 5.0 41 42#: Supported alignment mode names. 43ALIGNMENT_MODES = frozenset({"none", "median", "ls_affine", "ls_disparity", "ls_log"}) 44 45 46def default_valid_mask(pred: np.ndarray, gt_m: np.ndarray) -> np.ndarray: 47 """Standard validity gate used by every alignment + metric step. 48 49 A pixel enters the alignment fit (and the downstream metric) iff: 50 51 * GT is finite (D435 holes encoded as NaN or 0 mm are excluded); 52 * GT is in the sensor working range (``DEPTH_MIN_M`` … ``DEPTH_MAX_M``); 53 * Prediction is finite; 54 * Prediction is positive. 55 """ 56 return ( 57 np.isfinite(gt_m) 58 & (gt_m > DEPTH_MIN_M) 59 & (gt_m < DEPTH_MAX_M) 60 & np.isfinite(pred) 61 & (pred > 0) 62 ) 63 64 65def align_pred_to_gt( 66 pred: np.ndarray, 67 gt_m: np.ndarray, 68 mode: str, 69 valid: Optional[np.ndarray] = None, 70) -> np.ndarray: 71 """Fit a per-frame transform from raw prediction to GT scale. 72 73 Parameters 74 ---------- 75 pred 76 Predicted depth (any 2-D float array; semantics depend on the 77 model — for relative models it's up-to-scale, for metric models 78 it's already in metres). 79 gt_m 80 Ground-truth depth in metres. 81 mode 82 One of: 83 84 * ``"none"`` — identity. Use for native-metric models. 85 * ``"median"`` — ``pred *= median(gt) / median(pred)``; 86 cheap rescale, single dof. 87 * ``"ls_affine"`` — least-squares fit ``a·pred + b`` to GT 88 in depth space. Two dof; standard alignment for relative 89 and up-to-scale models (DA-V2 relative, MiDaS, Marigold, 90 Lotus). 91 * ``"ls_disparity"`` — least-squares fit of a model's native 92 inverse-depth/disparity output to GT disparity, followed by 93 conversion to metric depth. 94 * ``"ls_log"`` — least-squares fit ``a*pred + b`` to 95 ``log(GT)``, then exponentiate. This is FE2E's published 96 ``norm_type=ln`` output convention. 97 valid 98 Optional pre-computed validity mask. If not provided, uses 99 :func:`default_valid_mask`. 100 101 Returns 102 ------- 103 np.ndarray 104 Aligned prediction with the same shape and dtype as ``pred``. 105 106 Raises 107 ------ 108 ConfigError 109 If ``mode`` isn't in :data:`ALIGNMENT_MODES`. 110 """ 111 if mode == "none" or pred is None or gt_m is None: 112 return pred 113 if mode not in ALIGNMENT_MODES: 114 raise ConfigError( 115 f"unknown alignment mode {mode!r}", 116 hint=f"Expected one of: {sorted(ALIGNMENT_MODES)}.", 117 ) 118 if valid is None: 119 valid = default_valid_mask(pred, gt_m) 120 if not valid.any(): 121 return pred 122 123 p = pred[valid].astype(np.float64) 124 g = gt_m[valid].astype(np.float64) 125 126 if mode == "median": 127 scale = float(np.median(g) / max(np.median(p), 1e-9)) 128 return (pred * scale).astype(np.float32) 129 130 if mode == "ls_affine": 131 A = np.column_stack([p, np.ones_like(p)]) 132 coef, *_ = np.linalg.lstsq(A, g, rcond=None) 133 a, b = float(coef[0]), float(coef[1]) 134 return (a * pred + b).astype(np.float32) 135 136 if mode == "ls_disparity": 137 eps = 1e-3 138 inv_g = 1.0 / np.maximum(g, eps) 139 A = np.column_stack([p, np.ones_like(p)]) 140 coef, *_ = np.linalg.lstsq(A, inv_g, rcond=None) 141 a, b = float(coef[0]), float(coef[1]) 142 aligned_disparity = a * pred.astype(np.float64) + b 143 return (1.0 / np.maximum(aligned_disparity, eps)).astype(np.float32) 144 145 if mode == "ls_log": 146 A = np.column_stack([p, np.ones_like(p)]) 147 coef, *_ = np.linalg.lstsq(A, np.log(g), rcond=None) 148 a, b = float(coef[0]), float(coef[1]) 149 log_depth = np.minimum(a * pred.astype(np.float64) + b, 5.0) 150 return np.exp(log_depth).astype(np.float32) 151 152 # Unreachable thanks to the ALIGNMENT_MODES gate above; raises 153 # defensively in case the gate is loosened later. 154 raise ConfigError( 155 f"unknown alignment mode {mode!r}", 156 hint="Add the mode to ALIGNMENT_MODES and implement its branch.", 157 ) 158 159 160def align_pred_to_gt_pooled( 161 pred_seq: np.ndarray, 162 gt_seq: np.ndarray, 163 mode: str, 164 valid_seq: Optional[np.ndarray] = None, 165) -> np.ndarray: 166 """Pooled scale+shift fit over an entire ``(scene, phase)`` cell. 167 168 Paper §3.3 (line 62) prescribes **per-scene** scale-and-shift 169 alignment for affine-invariant depth models (Lotus-2, FE2E, etc.). 170 "Per-scene" here means one ``(s, t)`` per ``(scene, phase)`` cell, 171 pooling every valid pixel across all frames in that cell into a 172 single least-squares solve. The same scope applies to: 173 174 * Video Depth (paper task ``video_depth``) — pool over ``(T, H, W)``. 175 * Image Depth (paper task ``monocular_depth``) — pool over the phase's 176 ~250 frames, with each ``(H, W)`` slice contributing its valid 177 pixels. 178 179 Per-frame alignment would let each frame find its own ``(s, t)``, 180 artificially smoothing over any temporal scale drift the model has 181 within a phase — hiding a real failure mode. Per-scene preserves 182 that drift and reports it through the per-frame metrics (which are 183 then averaged into the cell). 184 185 Parameters 186 ---------- 187 pred_seq 188 Predicted depth for the cell. Either ``(T, H, W)`` (a phase 189 clip) or a list of per-frame arrays stacked along axis 0. Any 190 2-D-shape-compatible array works; only the first axis is 191 treated as the frame index for masking purposes. 192 gt_seq 193 Ground-truth depth in metres, same shape as ``pred_seq``. 194 mode 195 One of :data:`ALIGNMENT_MODES`. ``"none"`` returns ``pred_seq`` 196 unchanged. ``"median"`` / ``"ls_affine"`` / ``"ls_disparity"`` 197 all pool valid pixels across frames before solving. 198 valid_seq 199 Optional pre-computed mask, same shape as ``pred_seq``. If 200 absent, computed per-frame with :func:`default_valid_mask` and 201 OR'd to form the pooled mask. 202 203 Returns 204 ------- 205 np.ndarray 206 Aligned prediction with the same shape and dtype as 207 ``pred_seq``. When the pooled valid set is empty, returns 208 ``pred_seq`` unchanged. 209 210 Notes 211 ----- 212 A native-metric model gets ``mode="none"`` (or any mode where the 213 solver finds ``s ≈ 1, t ≈ 0`` because the prediction already 214 matches the GT distribution). An affine-invariant model gets 215 ``mode="ls_affine"``. The runner is expected to dispatch on the 216 adapter's ``depth_output_kind`` attribute. 217 """ 218 if mode == "none": 219 return pred_seq 220 if mode not in ALIGNMENT_MODES: 221 raise ConfigError( 222 f"unknown alignment mode {mode!r}", 223 hint=f"Expected one of: {sorted(ALIGNMENT_MODES)}.", 224 ) 225 if pred_seq.shape != gt_seq.shape: 226 raise ConfigError( 227 f"align_pred_to_gt_pooled: pred_seq.shape={pred_seq.shape} " 228 f"does not match gt_seq.shape={gt_seq.shape}", 229 hint="Both inputs must be the same shape (typically (T, H, W)).", 230 ) 231 if valid_seq is None: 232 # Compute per-frame masks and stack — equivalent to computing 233 # one mask over the whole pooled array, but reuses the existing 234 # default_valid_mask policy without making it sequence-aware. 235 valid_seq = default_valid_mask(pred_seq, gt_seq) 236 if valid_seq.shape != pred_seq.shape: 237 raise ConfigError( 238 f"align_pred_to_gt_pooled: valid_seq.shape={valid_seq.shape} " 239 f"does not match pred_seq.shape={pred_seq.shape}", 240 hint="The validity mask must be the same shape as the depth tensors.", 241 ) 242 if not valid_seq.any(): 243 return pred_seq 244 245 # Flatten across (T, H, W) and let the existing per-frame solver 246 # do the math. We tag the prediction's dtype so the caller's 247 # contract (float32 / float64) is preserved. 248 p = pred_seq[valid_seq].astype(np.float64) 249 g = gt_seq[valid_seq].astype(np.float64) 250 if p.size == 0: 251 return pred_seq 252 253 if mode == "median": 254 scale = float(np.median(g) / max(np.median(p), 1e-9)) 255 return (pred_seq * scale).astype(pred_seq.dtype) 256 257 if mode == "ls_affine": 258 A = np.column_stack([p, np.ones_like(p)]) 259 coef, *_ = np.linalg.lstsq(A, g, rcond=None) 260 a, b = float(coef[0]), float(coef[1]) 261 return (a * pred_seq + b).astype(pred_seq.dtype) 262 263 if mode == "ls_disparity": 264 eps = 1e-3 265 inv_g = 1.0 / np.maximum(g, eps) 266 A = np.column_stack([p, np.ones_like(p)]) 267 coef, *_ = np.linalg.lstsq(A, inv_g, rcond=None) 268 a, b = float(coef[0]), float(coef[1]) 269 aligned_disparity = a * pred_seq.astype(np.float64) + b 270 return (1.0 / np.maximum(aligned_disparity, eps)).astype(pred_seq.dtype) 271 272 if mode == "ls_log": 273 A = np.column_stack([p, np.ones_like(p)]) 274 coef, *_ = np.linalg.lstsq(A, np.log(g), rcond=None) 275 a, b = float(coef[0]), float(coef[1]) 276 log_depth = np.minimum(a * pred_seq.astype(np.float64) + b, 5.0) 277 return np.exp(log_depth).astype(pred_seq.dtype) 278 279 raise ConfigError( # pragma: no cover — guarded by ALIGNMENT_MODES gate 280 f"unknown alignment mode {mode!r}", 281 hint="Add the mode to ALIGNMENT_MODES and implement its branch.", 282 )
66def align_pred_to_gt( 67 pred: np.ndarray, 68 gt_m: np.ndarray, 69 mode: str, 70 valid: Optional[np.ndarray] = None, 71) -> np.ndarray: 72 """Fit a per-frame transform from raw prediction to GT scale. 73 74 Parameters 75 ---------- 76 pred 77 Predicted depth (any 2-D float array; semantics depend on the 78 model — for relative models it's up-to-scale, for metric models 79 it's already in metres). 80 gt_m 81 Ground-truth depth in metres. 82 mode 83 One of: 84 85 * ``"none"`` — identity. Use for native-metric models. 86 * ``"median"`` — ``pred *= median(gt) / median(pred)``; 87 cheap rescale, single dof. 88 * ``"ls_affine"`` — least-squares fit ``a·pred + b`` to GT 89 in depth space. Two dof; standard alignment for relative 90 and up-to-scale models (DA-V2 relative, MiDaS, Marigold, 91 Lotus). 92 * ``"ls_disparity"`` — least-squares fit of a model's native 93 inverse-depth/disparity output to GT disparity, followed by 94 conversion to metric depth. 95 * ``"ls_log"`` — least-squares fit ``a*pred + b`` to 96 ``log(GT)``, then exponentiate. This is FE2E's published 97 ``norm_type=ln`` output convention. 98 valid 99 Optional pre-computed validity mask. If not provided, uses 100 :func:`default_valid_mask`. 101 102 Returns 103 ------- 104 np.ndarray 105 Aligned prediction with the same shape and dtype as ``pred``. 106 107 Raises 108 ------ 109 ConfigError 110 If ``mode`` isn't in :data:`ALIGNMENT_MODES`. 111 """ 112 if mode == "none" or pred is None or gt_m is None: 113 return pred 114 if mode not in ALIGNMENT_MODES: 115 raise ConfigError( 116 f"unknown alignment mode {mode!r}", 117 hint=f"Expected one of: {sorted(ALIGNMENT_MODES)}.", 118 ) 119 if valid is None: 120 valid = default_valid_mask(pred, gt_m) 121 if not valid.any(): 122 return pred 123 124 p = pred[valid].astype(np.float64) 125 g = gt_m[valid].astype(np.float64) 126 127 if mode == "median": 128 scale = float(np.median(g) / max(np.median(p), 1e-9)) 129 return (pred * scale).astype(np.float32) 130 131 if mode == "ls_affine": 132 A = np.column_stack([p, np.ones_like(p)]) 133 coef, *_ = np.linalg.lstsq(A, g, rcond=None) 134 a, b = float(coef[0]), float(coef[1]) 135 return (a * pred + b).astype(np.float32) 136 137 if mode == "ls_disparity": 138 eps = 1e-3 139 inv_g = 1.0 / np.maximum(g, eps) 140 A = np.column_stack([p, np.ones_like(p)]) 141 coef, *_ = np.linalg.lstsq(A, inv_g, rcond=None) 142 a, b = float(coef[0]), float(coef[1]) 143 aligned_disparity = a * pred.astype(np.float64) + b 144 return (1.0 / np.maximum(aligned_disparity, eps)).astype(np.float32) 145 146 if mode == "ls_log": 147 A = np.column_stack([p, np.ones_like(p)]) 148 coef, *_ = np.linalg.lstsq(A, np.log(g), rcond=None) 149 a, b = float(coef[0]), float(coef[1]) 150 log_depth = np.minimum(a * pred.astype(np.float64) + b, 5.0) 151 return np.exp(log_depth).astype(np.float32) 152 153 # Unreachable thanks to the ALIGNMENT_MODES gate above; raises 154 # defensively in case the gate is loosened later. 155 raise ConfigError( 156 f"unknown alignment mode {mode!r}", 157 hint="Add the mode to ALIGNMENT_MODES and implement its branch.", 158 )
Fit a per-frame transform from raw prediction to GT scale.
Parameters
pred Predicted depth (any 2-D float array; semantics depend on the model — for relative models it's up-to-scale, for metric models it's already in metres). gt_m Ground-truth depth in metres. mode One of:
* ``"none"`` — identity. Use for native-metric models.
* ``"median"`` — ``pred *= median(gt) / median(pred)``;
cheap rescale, single dof.
* ``"ls_affine"`` — least-squares fit ``a·pred + b`` to GT
in depth space. Two dof; standard alignment for relative
and up-to-scale models (DA-V2 relative, MiDaS, Marigold,
Lotus).
* ``"ls_disparity"`` — least-squares fit of a model's native
inverse-depth/disparity output to GT disparity, followed by
conversion to metric depth.
* ``"ls_log"`` — least-squares fit ``a*pred + b`` to
``log(GT)``, then exponentiate. This is FE2E's published
``norm_type=ln`` output convention.
valid
Optional pre-computed validity mask. If not provided, uses
default_valid_mask().
Returns
np.ndarray
Aligned prediction with the same shape and dtype as pred.
Raises
ConfigError
If mode isn't in ALIGNMENT_MODES.
161def align_pred_to_gt_pooled( 162 pred_seq: np.ndarray, 163 gt_seq: np.ndarray, 164 mode: str, 165 valid_seq: Optional[np.ndarray] = None, 166) -> np.ndarray: 167 """Pooled scale+shift fit over an entire ``(scene, phase)`` cell. 168 169 Paper §3.3 (line 62) prescribes **per-scene** scale-and-shift 170 alignment for affine-invariant depth models (Lotus-2, FE2E, etc.). 171 "Per-scene" here means one ``(s, t)`` per ``(scene, phase)`` cell, 172 pooling every valid pixel across all frames in that cell into a 173 single least-squares solve. The same scope applies to: 174 175 * Video Depth (paper task ``video_depth``) — pool over ``(T, H, W)``. 176 * Image Depth (paper task ``monocular_depth``) — pool over the phase's 177 ~250 frames, with each ``(H, W)`` slice contributing its valid 178 pixels. 179 180 Per-frame alignment would let each frame find its own ``(s, t)``, 181 artificially smoothing over any temporal scale drift the model has 182 within a phase — hiding a real failure mode. Per-scene preserves 183 that drift and reports it through the per-frame metrics (which are 184 then averaged into the cell). 185 186 Parameters 187 ---------- 188 pred_seq 189 Predicted depth for the cell. Either ``(T, H, W)`` (a phase 190 clip) or a list of per-frame arrays stacked along axis 0. Any 191 2-D-shape-compatible array works; only the first axis is 192 treated as the frame index for masking purposes. 193 gt_seq 194 Ground-truth depth in metres, same shape as ``pred_seq``. 195 mode 196 One of :data:`ALIGNMENT_MODES`. ``"none"`` returns ``pred_seq`` 197 unchanged. ``"median"`` / ``"ls_affine"`` / ``"ls_disparity"`` 198 all pool valid pixels across frames before solving. 199 valid_seq 200 Optional pre-computed mask, same shape as ``pred_seq``. If 201 absent, computed per-frame with :func:`default_valid_mask` and 202 OR'd to form the pooled mask. 203 204 Returns 205 ------- 206 np.ndarray 207 Aligned prediction with the same shape and dtype as 208 ``pred_seq``. When the pooled valid set is empty, returns 209 ``pred_seq`` unchanged. 210 211 Notes 212 ----- 213 A native-metric model gets ``mode="none"`` (or any mode where the 214 solver finds ``s ≈ 1, t ≈ 0`` because the prediction already 215 matches the GT distribution). An affine-invariant model gets 216 ``mode="ls_affine"``. The runner is expected to dispatch on the 217 adapter's ``depth_output_kind`` attribute. 218 """ 219 if mode == "none": 220 return pred_seq 221 if mode not in ALIGNMENT_MODES: 222 raise ConfigError( 223 f"unknown alignment mode {mode!r}", 224 hint=f"Expected one of: {sorted(ALIGNMENT_MODES)}.", 225 ) 226 if pred_seq.shape != gt_seq.shape: 227 raise ConfigError( 228 f"align_pred_to_gt_pooled: pred_seq.shape={pred_seq.shape} " 229 f"does not match gt_seq.shape={gt_seq.shape}", 230 hint="Both inputs must be the same shape (typically (T, H, W)).", 231 ) 232 if valid_seq is None: 233 # Compute per-frame masks and stack — equivalent to computing 234 # one mask over the whole pooled array, but reuses the existing 235 # default_valid_mask policy without making it sequence-aware. 236 valid_seq = default_valid_mask(pred_seq, gt_seq) 237 if valid_seq.shape != pred_seq.shape: 238 raise ConfigError( 239 f"align_pred_to_gt_pooled: valid_seq.shape={valid_seq.shape} " 240 f"does not match pred_seq.shape={pred_seq.shape}", 241 hint="The validity mask must be the same shape as the depth tensors.", 242 ) 243 if not valid_seq.any(): 244 return pred_seq 245 246 # Flatten across (T, H, W) and let the existing per-frame solver 247 # do the math. We tag the prediction's dtype so the caller's 248 # contract (float32 / float64) is preserved. 249 p = pred_seq[valid_seq].astype(np.float64) 250 g = gt_seq[valid_seq].astype(np.float64) 251 if p.size == 0: 252 return pred_seq 253 254 if mode == "median": 255 scale = float(np.median(g) / max(np.median(p), 1e-9)) 256 return (pred_seq * scale).astype(pred_seq.dtype) 257 258 if mode == "ls_affine": 259 A = np.column_stack([p, np.ones_like(p)]) 260 coef, *_ = np.linalg.lstsq(A, g, rcond=None) 261 a, b = float(coef[0]), float(coef[1]) 262 return (a * pred_seq + b).astype(pred_seq.dtype) 263 264 if mode == "ls_disparity": 265 eps = 1e-3 266 inv_g = 1.0 / np.maximum(g, eps) 267 A = np.column_stack([p, np.ones_like(p)]) 268 coef, *_ = np.linalg.lstsq(A, inv_g, rcond=None) 269 a, b = float(coef[0]), float(coef[1]) 270 aligned_disparity = a * pred_seq.astype(np.float64) + b 271 return (1.0 / np.maximum(aligned_disparity, eps)).astype(pred_seq.dtype) 272 273 if mode == "ls_log": 274 A = np.column_stack([p, np.ones_like(p)]) 275 coef, *_ = np.linalg.lstsq(A, np.log(g), rcond=None) 276 a, b = float(coef[0]), float(coef[1]) 277 log_depth = np.minimum(a * pred_seq.astype(np.float64) + b, 5.0) 278 return np.exp(log_depth).astype(pred_seq.dtype) 279 280 raise ConfigError( # pragma: no cover — guarded by ALIGNMENT_MODES gate 281 f"unknown alignment mode {mode!r}", 282 hint="Add the mode to ALIGNMENT_MODES and implement its branch.", 283 )
Pooled scale+shift fit over an entire (scene, phase) cell.
Paper §3.3 (line 62) prescribes per-scene scale-and-shift
alignment for affine-invariant depth models (Lotus-2, FE2E, etc.).
"Per-scene" here means one (s, t) per (scene, phase) cell,
pooling every valid pixel across all frames in that cell into a
single least-squares solve. The same scope applies to:
- Video Depth (paper task
video_depth) — pool over(T, H, W). - Image Depth (paper task
monocular_depth) — pool over the phase's ~250 frames, with each(H, W)slice contributing its valid pixels.
Per-frame alignment would let each frame find its own (s, t),
artificially smoothing over any temporal scale drift the model has
within a phase — hiding a real failure mode. Per-scene preserves
that drift and reports it through the per-frame metrics (which are
then averaged into the cell).
Parameters
pred_seq
Predicted depth for the cell. Either (T, H, W) (a phase
clip) or a list of per-frame arrays stacked along axis 0. Any
2-D-shape-compatible array works; only the first axis is
treated as the frame index for masking purposes.
gt_seq
Ground-truth depth in metres, same shape as pred_seq.
mode
One of ALIGNMENT_MODES. "none" returns pred_seq
unchanged. "median" / "ls_affine" / "ls_disparity"
all pool valid pixels across frames before solving.
valid_seq
Optional pre-computed mask, same shape as pred_seq. If
absent, computed per-frame with default_valid_mask() and
OR'd to form the pooled mask.
Returns
np.ndarray
Aligned prediction with the same shape and dtype as
pred_seq. When the pooled valid set is empty, returns
pred_seq unchanged.
Notes
A native-metric model gets mode="none" (or any mode where the
solver finds s ≈ 1, t ≈ 0 because the prediction already
matches the GT distribution). An affine-invariant model gets
mode="ls_affine". The runner is expected to dispatch on the
adapter's depth_output_kind attribute.
47def default_valid_mask(pred: np.ndarray, gt_m: np.ndarray) -> np.ndarray: 48 """Standard validity gate used by every alignment + metric step. 49 50 A pixel enters the alignment fit (and the downstream metric) iff: 51 52 * GT is finite (D435 holes encoded as NaN or 0 mm are excluded); 53 * GT is in the sensor working range (``DEPTH_MIN_M`` … ``DEPTH_MAX_M``); 54 * Prediction is finite; 55 * Prediction is positive. 56 """ 57 return ( 58 np.isfinite(gt_m) 59 & (gt_m > DEPTH_MIN_M) 60 & (gt_m < DEPTH_MAX_M) 61 & np.isfinite(pred) 62 & (pred > 0) 63 )
Standard validity gate used by every alignment + metric step.
A pixel enters the alignment fit (and the downstream metric) iff:
- GT is finite (D435 holes encoded as NaN or 0 mm are excluded);
- GT is in the sensor working range (
DEPTH_MIN_M…DEPTH_MAX_M); - Prediction is finite;
- Prediction is positive.