euler-eval 2.12.0__tar.gz → 2.13.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {euler_eval-2.12.0 → euler_eval-2.13.0}/PKG-INFO +11 -4
- {euler_eval-2.12.0 → euler_eval-2.13.0}/README.md +10 -3
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/cli.py +45 -5
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/data.py +68 -10
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/evaluate.py +213 -11
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval.egg-info/PKG-INFO +11 -4
- {euler_eval-2.12.0 → euler_eval-2.13.0}/pyproject.toml +1 -1
- {euler_eval-2.12.0 → euler_eval-2.13.0}/tests/test_config.py +52 -1
- {euler_eval-2.12.0 → euler_eval-2.13.0}/tests/test_data_builders.py +85 -1
- {euler_eval-2.12.0 → euler_eval-2.13.0}/tests/test_sparse_depth.py +104 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/__init__.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/config_paths.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/metrics/__init__.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/metrics/absrel.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/metrics/daniel_error.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/metrics/depth_binned_error.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/metrics/depth_edge_f1.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/metrics/depth_standard.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/metrics/fid_kid.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/metrics/gpu_depth_batch.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/metrics/gpu_image_batch.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/metrics/high_freq_energy.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/metrics/lpips_metric.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/metrics/normal_consistency.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/metrics/psnr.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/metrics/rgb_edge_f1.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/metrics/rgb_lpips.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/metrics/rgb_psnr_ssim.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/metrics/rho_a.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/metrics/rmse.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/metrics/scale_invariant_log.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/metrics/ssim.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/metrics/tail_errors.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/metrics/utils.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/sanity_checker.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval/utils/hierarchy_parser.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval.egg-info/SOURCES.txt +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval.egg-info/dependency_links.txt +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval.egg-info/entry_points.txt +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval.egg-info/requires.txt +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/euler_eval.egg-info/top_level.txt +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/init_cache.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/setup.cfg +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/tests/test_alignment.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/tests/test_cli_device.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/tests/test_data.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/tests/test_depth_alignment_output.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/tests/test_depth_standard.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/tests/test_evaluate_helpers.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/tests/test_init_cache.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/tests/test_integration.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/tests/test_meta_output.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/tests/test_metrics_utils.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/tests/test_rgb_fid_output.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/tests/test_rho_a.py +0 -0
- {euler_eval-2.12.0 → euler_eval-2.13.0}/tests/test_save_results.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: euler-eval
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.13.0
|
|
4
4
|
Summary: Depth map evaluation toolkit with comprehensive metrics
|
|
5
5
|
Author: Depth Eval Contributors
|
|
6
6
|
License: MIT
|
|
@@ -149,7 +149,7 @@ This pre-downloads:
|
|
|
149
149
|
# Evaluate with default settings (auto-selects CUDA when available)
|
|
150
150
|
depth-eval config.json --batch-size 32
|
|
151
151
|
|
|
152
|
-
# Evaluate with sky masking enabled (requires gt.segmentation
|
|
152
|
+
# Evaluate with sky masking enabled (requires gt.segmentation or gt.semantic_segmentation)
|
|
153
153
|
depth-eval config.json --mask-sky -v
|
|
154
154
|
|
|
155
155
|
# Skip RGB evaluation, only evaluate depth
|
|
@@ -241,7 +241,13 @@ Defines GT modalities, prediction datasets to evaluate, and optional euler_train
|
|
|
241
241
|
|
|
242
242
|
Each modality entry can optionally include a `split` field to select a specific split from the dataset (e.g. `{ "path": "/data/gt/depth", "split": "test" }`). euler-loading inline selectors are also accepted in `path`, such as `/data/muses.zip:test` or `/data/muses.zip:test#scope=rgb`.
|
|
243
243
|
|
|
244
|
-
For sparse pointcloud depth evaluation, use `gt.sparse_depth` instead of `gt.depth`. The prediction uses a dense depth-like map under `datasets[].depth`, `datasets[].relative_depth`, or `datasets[].affine_depth`. The evaluator projects the sparse GT point cloud into the prediction image plane using `gt.intrinsics` and `gt.camera_extrinsics`, then computes pointwise depth metrics only at projected valid pixels.
|
|
244
|
+
For sparse pointcloud depth evaluation, use `gt.sparse_depth` instead of `gt.depth`. The prediction uses a dense depth-like map under `datasets[].depth`, `datasets[].relative_depth`, or `datasets[].affine_depth`. The evaluator projects the sparse GT point cloud into the prediction image plane using `gt.intrinsics` and `gt.camera_extrinsics`, then computes pointwise depth metrics only at projected valid pixels. For MUSES through `euler_loading.loaders.muses`, `gt.camera_extrinsics` normally resolves to the direct `lidar2rgb` transform and no extra lidar pose is needed. If a dataset exposes separate lidar and camera poses in a shared frame instead, provide optional `gt.lidar_extrinsics`; the evaluator composes `inv(camera_pose) @ lidar_pose` before projection.
|
|
245
|
+
|
|
246
|
+
Sparse depth does not require segmentation GT. `gt.segmentation` and
|
|
247
|
+
`gt.semantic_segmentation` are optional aliases for the same sky-mask source.
|
|
248
|
+
They are loaded only when `--mask-sky` is set; in sparse depth evaluation that
|
|
249
|
+
mask excludes sky pixels from projected-point metrics and from scale/shift
|
|
250
|
+
fitting. Without `--mask-sky`, the segmentation entry is not loaded or used.
|
|
245
251
|
|
|
246
252
|
#### GT section
|
|
247
253
|
|
|
@@ -251,10 +257,11 @@ For sparse pointcloud depth evaluation, use `gt.sparse_depth` instead of `gt.dep
|
|
|
251
257
|
| `gt.depth.path` | no\* | Path to GT depth dataset |
|
|
252
258
|
| `gt.sparse_depth.path` | no\* | Path to sparse pointcloud GT dataset, e.g. `sparse_depth` with `(N,C)` points whose first columns are `x,y,z` in meters |
|
|
253
259
|
| `gt.rays.path` | no\* | Path to GT ray direction map dataset (for rays evaluation) |
|
|
254
|
-
| `gt.segmentation.path` | no | Path to GT segmentation (needed for `--mask-sky
|
|
260
|
+
| `gt.segmentation.path` / `gt.semantic_segmentation.path` | no | Path to GT segmentation (needed for `--mask-sky`; use one key, not both) |
|
|
255
261
|
| `gt.calibration.path` | no | Path to calibration data (camera intrinsics matrices) |
|
|
256
262
|
| `gt.intrinsics.path` | required with `gt.sparse_depth` | Path to camera intrinsics matrices for pointcloud projection |
|
|
257
263
|
| `gt.camera_extrinsics.path` | required with `gt.sparse_depth` | Path to source-to-camera extrinsics, e.g. MUSES `lidar2rgb`, for pointcloud projection |
|
|
264
|
+
| `gt.lidar_extrinsics.path` | no | Optional lidar sensor pose in the same shared frame as `gt.camera_extrinsics`; when provided, `gt.camera_extrinsics` is interpreted as the camera pose and both are composed for projection |
|
|
258
265
|
| `gt.name` | no | Display name for ground truth (default: `"GT"`) |
|
|
259
266
|
|
|
260
267
|
\* At least one of `gt.rgb.path`, `gt.depth.path`, `gt.sparse_depth.path`, or `gt.rays.path` is required.
|
|
@@ -111,7 +111,7 @@ This pre-downloads:
|
|
|
111
111
|
# Evaluate with default settings (auto-selects CUDA when available)
|
|
112
112
|
depth-eval config.json --batch-size 32
|
|
113
113
|
|
|
114
|
-
# Evaluate with sky masking enabled (requires gt.segmentation
|
|
114
|
+
# Evaluate with sky masking enabled (requires gt.segmentation or gt.semantic_segmentation)
|
|
115
115
|
depth-eval config.json --mask-sky -v
|
|
116
116
|
|
|
117
117
|
# Skip RGB evaluation, only evaluate depth
|
|
@@ -203,7 +203,13 @@ Defines GT modalities, prediction datasets to evaluate, and optional euler_train
|
|
|
203
203
|
|
|
204
204
|
Each modality entry can optionally include a `split` field to select a specific split from the dataset (e.g. `{ "path": "/data/gt/depth", "split": "test" }`). euler-loading inline selectors are also accepted in `path`, such as `/data/muses.zip:test` or `/data/muses.zip:test#scope=rgb`.
|
|
205
205
|
|
|
206
|
-
For sparse pointcloud depth evaluation, use `gt.sparse_depth` instead of `gt.depth`. The prediction uses a dense depth-like map under `datasets[].depth`, `datasets[].relative_depth`, or `datasets[].affine_depth`. The evaluator projects the sparse GT point cloud into the prediction image plane using `gt.intrinsics` and `gt.camera_extrinsics`, then computes pointwise depth metrics only at projected valid pixels.
|
|
206
|
+
For sparse pointcloud depth evaluation, use `gt.sparse_depth` instead of `gt.depth`. The prediction uses a dense depth-like map under `datasets[].depth`, `datasets[].relative_depth`, or `datasets[].affine_depth`. The evaluator projects the sparse GT point cloud into the prediction image plane using `gt.intrinsics` and `gt.camera_extrinsics`, then computes pointwise depth metrics only at projected valid pixels. For MUSES through `euler_loading.loaders.muses`, `gt.camera_extrinsics` normally resolves to the direct `lidar2rgb` transform and no extra lidar pose is needed. If a dataset exposes separate lidar and camera poses in a shared frame instead, provide optional `gt.lidar_extrinsics`; the evaluator composes `inv(camera_pose) @ lidar_pose` before projection.
|
|
207
|
+
|
|
208
|
+
Sparse depth does not require segmentation GT. `gt.segmentation` and
|
|
209
|
+
`gt.semantic_segmentation` are optional aliases for the same sky-mask source.
|
|
210
|
+
They are loaded only when `--mask-sky` is set; in sparse depth evaluation that
|
|
211
|
+
mask excludes sky pixels from projected-point metrics and from scale/shift
|
|
212
|
+
fitting. Without `--mask-sky`, the segmentation entry is not loaded or used.
|
|
207
213
|
|
|
208
214
|
#### GT section
|
|
209
215
|
|
|
@@ -213,10 +219,11 @@ For sparse pointcloud depth evaluation, use `gt.sparse_depth` instead of `gt.dep
|
|
|
213
219
|
| `gt.depth.path` | no\* | Path to GT depth dataset |
|
|
214
220
|
| `gt.sparse_depth.path` | no\* | Path to sparse pointcloud GT dataset, e.g. `sparse_depth` with `(N,C)` points whose first columns are `x,y,z` in meters |
|
|
215
221
|
| `gt.rays.path` | no\* | Path to GT ray direction map dataset (for rays evaluation) |
|
|
216
|
-
| `gt.segmentation.path` | no | Path to GT segmentation (needed for `--mask-sky
|
|
222
|
+
| `gt.segmentation.path` / `gt.semantic_segmentation.path` | no | Path to GT segmentation (needed for `--mask-sky`; use one key, not both) |
|
|
217
223
|
| `gt.calibration.path` | no | Path to calibration data (camera intrinsics matrices) |
|
|
218
224
|
| `gt.intrinsics.path` | required with `gt.sparse_depth` | Path to camera intrinsics matrices for pointcloud projection |
|
|
219
225
|
| `gt.camera_extrinsics.path` | required with `gt.sparse_depth` | Path to source-to-camera extrinsics, e.g. MUSES `lidar2rgb`, for pointcloud projection |
|
|
226
|
+
| `gt.lidar_extrinsics.path` | no | Optional lidar sensor pose in the same shared frame as `gt.camera_extrinsics`; when provided, `gt.camera_extrinsics` is interpreted as the camera pose and both are composed for projection |
|
|
220
227
|
| `gt.name` | no | Display name for ground truth (default: `"GT"`) |
|
|
221
228
|
|
|
222
229
|
\* At least one of `gt.rgb.path`, `gt.depth.path`, `gt.sparse_depth.path`, or `gt.rays.path` is required.
|
|
@@ -152,6 +152,7 @@ def _rgb_eval_axes(*, benchmark: bool = False) -> dict[str, AxisDeclaration]:
|
|
|
152
152
|
_RAYS_EVAL_AXES: dict[str, AxisDeclaration] = {}
|
|
153
153
|
|
|
154
154
|
_PRED_DEPTH_KEYS = ("depth", "relative_depth", "affine_depth")
|
|
155
|
+
_GT_SEGMENTATION_KEYS = ("segmentation", "semantic_segmentation")
|
|
155
156
|
|
|
156
157
|
# Downstream eval consumers validate metricSet.metricNamespace with a stricter
|
|
157
158
|
# first-segment rule than euler_metric_naming's modality validator. Keep the
|
|
@@ -592,6 +593,24 @@ def _prediction_depth_space_hint(key: str | None) -> str | None:
|
|
|
592
593
|
return None
|
|
593
594
|
|
|
594
595
|
|
|
596
|
+
def _gt_segmentation_entry(gt: dict) -> tuple[str | None, dict | None]:
|
|
597
|
+
"""Return the configured GT segmentation entry, accepting legacy aliases."""
|
|
598
|
+
matches = [
|
|
599
|
+
key
|
|
600
|
+
for key in _GT_SEGMENTATION_KEYS
|
|
601
|
+
if key in gt and "path" in gt.get(key, {})
|
|
602
|
+
]
|
|
603
|
+
if not matches:
|
|
604
|
+
return None, None
|
|
605
|
+
if len(matches) > 1:
|
|
606
|
+
raise ValueError(
|
|
607
|
+
"gt contains multiple segmentation entries "
|
|
608
|
+
f"{matches}. Use only one of {list(_GT_SEGMENTATION_KEYS)}."
|
|
609
|
+
)
|
|
610
|
+
key = matches[0]
|
|
611
|
+
return key, gt[key]
|
|
612
|
+
|
|
613
|
+
|
|
595
614
|
def validate_gt_config(gt: dict) -> None:
|
|
596
615
|
"""Validate the ``gt`` section of the configuration.
|
|
597
616
|
|
|
@@ -619,15 +638,19 @@ def validate_gt_config(gt: dict) -> None:
|
|
|
619
638
|
"gt.camera_extrinsics.path for pointcloud projection"
|
|
620
639
|
)
|
|
621
640
|
|
|
641
|
+
_gt_segmentation_entry(gt)
|
|
642
|
+
|
|
622
643
|
for modality in (
|
|
623
644
|
"rgb",
|
|
624
645
|
"depth",
|
|
625
646
|
"sparse_depth",
|
|
626
647
|
"rays",
|
|
627
648
|
"segmentation",
|
|
649
|
+
"semantic_segmentation",
|
|
628
650
|
"calibration",
|
|
629
651
|
"intrinsics",
|
|
630
652
|
"camera_extrinsics",
|
|
653
|
+
"lidar_extrinsics",
|
|
631
654
|
):
|
|
632
655
|
if modality in gt and "path" in gt[modality]:
|
|
633
656
|
p = normalize_modality_path(
|
|
@@ -962,16 +985,18 @@ def main():
|
|
|
962
985
|
print("-" * 60)
|
|
963
986
|
|
|
964
987
|
# Check sky masking prerequisites
|
|
988
|
+
segmentation_key, segmentation_entry = _gt_segmentation_entry(config["gt"])
|
|
965
989
|
if args.mask_sky:
|
|
966
|
-
if
|
|
990
|
+
if segmentation_entry is None:
|
|
967
991
|
print(
|
|
968
|
-
"Warning: --mask-sky requires gt.segmentation
|
|
992
|
+
"Warning: --mask-sky requires gt.segmentation or "
|
|
993
|
+
"gt.semantic_segmentation in config. "
|
|
969
994
|
"Sky masking disabled.",
|
|
970
995
|
file=sys.stderr,
|
|
971
996
|
)
|
|
972
997
|
args.mask_sky = False
|
|
973
998
|
else:
|
|
974
|
-
print("Sky masking enabled")
|
|
999
|
+
print(f"Sky masking enabled ({segmentation_key})")
|
|
975
1000
|
print("-" * 60)
|
|
976
1001
|
|
|
977
1002
|
# Initialize sanity checker if not disabled
|
|
@@ -1006,8 +1031,11 @@ def main():
|
|
|
1006
1031
|
calibration_path = gt.get("calibration", {}).get("path")
|
|
1007
1032
|
intrinsics_path = gt.get("intrinsics", {}).get("path")
|
|
1008
1033
|
camera_extrinsics_path = gt.get("camera_extrinsics", {}).get("path")
|
|
1034
|
+
lidar_extrinsics_path = gt.get("lidar_extrinsics", {}).get("path")
|
|
1009
1035
|
segmentation_path = (
|
|
1010
|
-
|
|
1036
|
+
segmentation_entry.get("path")
|
|
1037
|
+
if args.mask_sky and segmentation_entry is not None
|
|
1038
|
+
else None
|
|
1011
1039
|
)
|
|
1012
1040
|
gt_depth_split = gt.get("depth", {}).get("split")
|
|
1013
1041
|
gt_sparse_depth_split = gt.get("sparse_depth", {}).get("split")
|
|
@@ -1016,8 +1044,11 @@ def main():
|
|
|
1016
1044
|
calibration_split = gt.get("calibration", {}).get("split")
|
|
1017
1045
|
intrinsics_split = gt.get("intrinsics", {}).get("split")
|
|
1018
1046
|
camera_extrinsics_split = gt.get("camera_extrinsics", {}).get("split")
|
|
1047
|
+
lidar_extrinsics_split = gt.get("lidar_extrinsics", {}).get("split")
|
|
1019
1048
|
segmentation_split = (
|
|
1020
|
-
|
|
1049
|
+
segmentation_entry.get("split")
|
|
1050
|
+
if args.mask_sky and segmentation_entry is not None
|
|
1051
|
+
else None
|
|
1021
1052
|
)
|
|
1022
1053
|
|
|
1023
1054
|
# Evaluate each prediction dataset
|
|
@@ -1063,6 +1094,7 @@ def main():
|
|
|
1063
1094
|
pred_depth_metadata_scope=(
|
|
1064
1095
|
pred_depth_key if pred_depth_key != "depth" else None
|
|
1065
1096
|
),
|
|
1097
|
+
segmentation_modality_key=segmentation_key or "segmentation",
|
|
1066
1098
|
gt_depth_split=gt_depth_split,
|
|
1067
1099
|
pred_depth_split=pred_depth_split,
|
|
1068
1100
|
calibration_split=calibration_split,
|
|
@@ -1242,20 +1274,25 @@ def main():
|
|
|
1242
1274
|
print(f" Pred depth entry: {pred_depth_key}")
|
|
1243
1275
|
print(f" Intrinsics: {intrinsics_path}")
|
|
1244
1276
|
print(f" Camera extrinsics: {camera_extrinsics_path}")
|
|
1277
|
+
if lidar_extrinsics_path:
|
|
1278
|
+
print(f" Lidar extrinsics: {lidar_extrinsics_path}")
|
|
1245
1279
|
|
|
1246
1280
|
sparse_depth_dataset = build_sparse_depth_eval_dataset(
|
|
1247
1281
|
gt_sparse_depth_path=gt_sparse_depth_path,
|
|
1248
1282
|
pred_depth_path=pred_depth_path,
|
|
1249
1283
|
intrinsics_path=intrinsics_path,
|
|
1250
1284
|
camera_extrinsics_path=camera_extrinsics_path,
|
|
1285
|
+
lidar_extrinsics_path=lidar_extrinsics_path,
|
|
1251
1286
|
segmentation_path=segmentation_path,
|
|
1252
1287
|
pred_depth_metadata_scope=(
|
|
1253
1288
|
pred_depth_key if pred_depth_key != "depth" else None
|
|
1254
1289
|
),
|
|
1290
|
+
segmentation_modality_key=segmentation_key or "segmentation",
|
|
1255
1291
|
gt_sparse_depth_split=gt_sparse_depth_split,
|
|
1256
1292
|
pred_depth_split=pred_depth_split,
|
|
1257
1293
|
intrinsics_split=intrinsics_split,
|
|
1258
1294
|
camera_extrinsics_split=camera_extrinsics_split,
|
|
1295
|
+
lidar_extrinsics_split=lidar_extrinsics_split,
|
|
1259
1296
|
segmentation_split=segmentation_split,
|
|
1260
1297
|
)
|
|
1261
1298
|
et_eval_datasets["sparse_depth"] = sparse_depth_dataset
|
|
@@ -1334,6 +1371,8 @@ def main():
|
|
|
1334
1371
|
"intrinsics_split": intrinsics_split,
|
|
1335
1372
|
"camera_extrinsics_path": camera_extrinsics_path,
|
|
1336
1373
|
"camera_extrinsics_split": camera_extrinsics_split,
|
|
1374
|
+
"lidar_extrinsics_path": lidar_extrinsics_path,
|
|
1375
|
+
"lidar_extrinsics_split": lidar_extrinsics_split,
|
|
1337
1376
|
},
|
|
1338
1377
|
"spatial_alignment": {
|
|
1339
1378
|
"method": sparse_depth_spatial.get(
|
|
@@ -1450,6 +1489,7 @@ def main():
|
|
|
1450
1489
|
gt_depth_split=gt_depth_split,
|
|
1451
1490
|
calibration_split=calibration_split,
|
|
1452
1491
|
segmentation_split=segmentation_split,
|
|
1492
|
+
segmentation_modality_key=segmentation_key or "segmentation",
|
|
1453
1493
|
)
|
|
1454
1494
|
et_eval_datasets["rgb"] = rgb_dataset
|
|
1455
1495
|
|
|
@@ -13,7 +13,7 @@ import torch
|
|
|
13
13
|
from ds_crawler import get_dataset_contract, index_dataset_from_path
|
|
14
14
|
from euler_loading import Modality, MultiModalDataset, resolve_loader_module
|
|
15
15
|
|
|
16
|
-
from .config_paths import build_modality
|
|
16
|
+
from .config_paths import build_modality, parse_modality_path
|
|
17
17
|
from .metrics.utils import convert_planar_to_radial
|
|
18
18
|
|
|
19
19
|
# ---------------------------------------------------------------------------
|
|
@@ -168,6 +168,26 @@ def to_numpy_extrinsics(data: Any) -> np.ndarray:
|
|
|
168
168
|
return arr.astype(np.float32, copy=False)
|
|
169
169
|
|
|
170
170
|
|
|
171
|
+
def compose_sensor_to_camera_extrinsics(
|
|
172
|
+
source_sensor_extrinsics: Any,
|
|
173
|
+
camera_extrinsics: Any,
|
|
174
|
+
) -> np.ndarray:
|
|
175
|
+
"""Compose per-sensor poses into a source-to-camera transform.
|
|
176
|
+
|
|
177
|
+
Both inputs are interpreted as sensor-to-common-frame transforms, e.g.
|
|
178
|
+
``T_common_from_lidar`` and ``T_common_from_camera``. The returned matrix
|
|
179
|
+
maps source sensor points directly into the camera frame:
|
|
180
|
+
|
|
181
|
+
``T_camera_from_source = inv(T_common_from_camera) @ T_common_from_source``.
|
|
182
|
+
"""
|
|
183
|
+
source_to_common = to_numpy_extrinsics(source_sensor_extrinsics).astype(
|
|
184
|
+
np.float64
|
|
185
|
+
)
|
|
186
|
+
camera_to_common = to_numpy_extrinsics(camera_extrinsics).astype(np.float64)
|
|
187
|
+
common_to_camera = np.linalg.inv(camera_to_common)
|
|
188
|
+
return (common_to_camera @ source_to_common).astype(np.float32)
|
|
189
|
+
|
|
190
|
+
|
|
171
191
|
def to_numpy_point_cloud(data: Any) -> np.ndarray:
|
|
172
192
|
"""Convert point-cloud data to ``(N, C)`` float32 numpy array.
|
|
173
193
|
|
|
@@ -518,7 +538,11 @@ def compute_scale_and_shift(
|
|
|
518
538
|
# ---------------------------------------------------------------------------
|
|
519
539
|
|
|
520
540
|
|
|
521
|
-
def _resolve_sky_mask_loader(
|
|
541
|
+
def _resolve_sky_mask_loader(
|
|
542
|
+
segmentation_path: str,
|
|
543
|
+
*,
|
|
544
|
+
modality_key: str = "segmentation",
|
|
545
|
+
) -> Callable[..., Any]:
|
|
522
546
|
"""Resolve the ``sky_mask`` loader for a segmentation dataset.
|
|
523
547
|
|
|
524
548
|
The segmentation dataset's ds-crawler index declares a loader module
|
|
@@ -526,7 +550,7 @@ def _resolve_sky_mask_loader(segmentation_path: str) -> Callable[..., Any]:
|
|
|
526
550
|
its ``sky_mask`` function so that raw class-segmentation data is
|
|
527
551
|
converted to a boolean sky mask at load time.
|
|
528
552
|
"""
|
|
529
|
-
modality = _modality(path=segmentation_path, modality_key=
|
|
553
|
+
modality = _modality(path=segmentation_path, modality_key=modality_key)
|
|
530
554
|
try:
|
|
531
555
|
index = index_dataset_from_path(
|
|
532
556
|
modality.path,
|
|
@@ -596,6 +620,7 @@ def build_depth_eval_dataset(
|
|
|
596
620
|
calibration_split: Optional[str] = None,
|
|
597
621
|
segmentation_split: Optional[str] = None,
|
|
598
622
|
pred_depth_metadata_scope: Optional[str] = None,
|
|
623
|
+
segmentation_modality_key: str = "segmentation",
|
|
599
624
|
) -> MultiModalDataset:
|
|
600
625
|
"""Build a MultiModalDataset for depth evaluation.
|
|
601
626
|
|
|
@@ -644,10 +669,13 @@ def build_depth_eval_dataset(
|
|
|
644
669
|
split=calibration_split,
|
|
645
670
|
)
|
|
646
671
|
if segmentation_path is not None:
|
|
647
|
-
sky_fn = _resolve_sky_mask_loader(
|
|
672
|
+
sky_fn = _resolve_sky_mask_loader(
|
|
673
|
+
segmentation_path,
|
|
674
|
+
modality_key=segmentation_modality_key,
|
|
675
|
+
)
|
|
648
676
|
hierarchical["segmentation"] = _modality(
|
|
649
677
|
path=segmentation_path,
|
|
650
|
-
modality_key=
|
|
678
|
+
modality_key=segmentation_modality_key,
|
|
651
679
|
loader=sky_fn,
|
|
652
680
|
split=segmentation_split,
|
|
653
681
|
)
|
|
@@ -663,22 +691,31 @@ def build_sparse_depth_eval_dataset(
|
|
|
663
691
|
pred_depth_path: str,
|
|
664
692
|
intrinsics_path: str,
|
|
665
693
|
camera_extrinsics_path: str,
|
|
694
|
+
lidar_extrinsics_path: Optional[str] = None,
|
|
666
695
|
segmentation_path: Optional[str] = None,
|
|
667
696
|
gt_sparse_depth_split: Optional[str] = None,
|
|
668
697
|
pred_depth_split: Optional[str] = None,
|
|
669
698
|
intrinsics_split: Optional[str] = None,
|
|
670
699
|
camera_extrinsics_split: Optional[str] = None,
|
|
700
|
+
lidar_extrinsics_split: Optional[str] = None,
|
|
671
701
|
segmentation_split: Optional[str] = None,
|
|
672
702
|
pred_depth_metadata_scope: Optional[str] = None,
|
|
703
|
+
segmentation_modality_key: str = "segmentation",
|
|
673
704
|
) -> MultiModalDataset:
|
|
674
705
|
"""Build a MultiModalDataset for sparse pointcloud depth evaluation.
|
|
675
706
|
|
|
676
707
|
The returned dataset yields dense predicted depth under ``"pred"``,
|
|
677
708
|
sparse GT point clouds under ``"gt"``, and hierarchical camera
|
|
678
709
|
calibration under ``"intrinsics"`` and ``"camera_extrinsics"``.
|
|
710
|
+
When ``lidar_extrinsics_path`` is supplied, ``camera_extrinsics`` and
|
|
711
|
+
``lidar_extrinsics`` are treated as per-sensor poses in a shared frame and
|
|
712
|
+
composed into the source-to-camera transform used for projection. Without
|
|
713
|
+
``lidar_extrinsics_path``, ``camera_extrinsics`` remains the direct
|
|
714
|
+
source-to-camera transform, such as MUSES ``lidar2rgb``.
|
|
679
715
|
``pred_depth_metadata_scope`` can select a depth-like prediction scope
|
|
680
716
|
such as ``relative_depth`` or ``affine_depth`` while still loading it
|
|
681
|
-
as a dense depth map.
|
|
717
|
+
as a dense depth map. Segmentation remains optional for sparse depth and is
|
|
718
|
+
only loaded by callers that request sky masking.
|
|
682
719
|
"""
|
|
683
720
|
modalities = {
|
|
684
721
|
"gt": _modality(
|
|
@@ -707,11 +744,28 @@ def build_sparse_depth_eval_dataset(
|
|
|
707
744
|
split=camera_extrinsics_split,
|
|
708
745
|
),
|
|
709
746
|
}
|
|
747
|
+
if lidar_extrinsics_path is not None:
|
|
748
|
+
lidar_scope = (
|
|
749
|
+
parse_modality_path(
|
|
750
|
+
lidar_extrinsics_path,
|
|
751
|
+
split=lidar_extrinsics_split,
|
|
752
|
+
).metadata_scope
|
|
753
|
+
or "lidar_extrinsics"
|
|
754
|
+
)
|
|
755
|
+
hierarchical["lidar_extrinsics"] = _modality(
|
|
756
|
+
path=lidar_extrinsics_path,
|
|
757
|
+
modality_key="camera_extrinsics",
|
|
758
|
+
metadata_scope=lidar_scope,
|
|
759
|
+
split=lidar_extrinsics_split,
|
|
760
|
+
)
|
|
710
761
|
if segmentation_path is not None:
|
|
711
|
-
sky_fn = _resolve_sky_mask_loader(
|
|
762
|
+
sky_fn = _resolve_sky_mask_loader(
|
|
763
|
+
segmentation_path,
|
|
764
|
+
modality_key=segmentation_modality_key,
|
|
765
|
+
)
|
|
712
766
|
hierarchical["segmentation"] = _modality(
|
|
713
767
|
path=segmentation_path,
|
|
714
|
-
modality_key=
|
|
768
|
+
modality_key=segmentation_modality_key,
|
|
715
769
|
loader=sky_fn,
|
|
716
770
|
split=segmentation_split,
|
|
717
771
|
)
|
|
@@ -733,6 +787,7 @@ def build_rgb_eval_dataset(
|
|
|
733
787
|
gt_depth_split: Optional[str] = None,
|
|
734
788
|
calibration_split: Optional[str] = None,
|
|
735
789
|
segmentation_split: Optional[str] = None,
|
|
790
|
+
segmentation_modality_key: str = "segmentation",
|
|
736
791
|
) -> MultiModalDataset:
|
|
737
792
|
"""Build a MultiModalDataset for RGB evaluation.
|
|
738
793
|
|
|
@@ -785,10 +840,13 @@ def build_rgb_eval_dataset(
|
|
|
785
840
|
split=calibration_split,
|
|
786
841
|
)
|
|
787
842
|
if segmentation_path is not None:
|
|
788
|
-
sky_fn = _resolve_sky_mask_loader(
|
|
843
|
+
sky_fn = _resolve_sky_mask_loader(
|
|
844
|
+
segmentation_path,
|
|
845
|
+
modality_key=segmentation_modality_key,
|
|
846
|
+
)
|
|
789
847
|
hierarchical["segmentation"] = _modality(
|
|
790
848
|
path=segmentation_path,
|
|
791
|
-
modality_key=
|
|
849
|
+
modality_key=segmentation_modality_key,
|
|
792
850
|
loader=sky_fn,
|
|
793
851
|
split=segmentation_split,
|
|
794
852
|
)
|
|
@@ -5,11 +5,11 @@ Runs all metrics over depth and RGB datasets loaded via euler_loading.
|
|
|
5
5
|
|
|
6
6
|
import copy
|
|
7
7
|
import tempfile
|
|
8
|
+
from collections.abc import Mapping
|
|
8
9
|
from pathlib import Path
|
|
10
|
+
from typing import Iterator, Optional, Tuple
|
|
9
11
|
|
|
10
12
|
import numpy as np
|
|
11
|
-
import torch
|
|
12
|
-
from typing import Iterator, Optional, Tuple
|
|
13
13
|
from PIL import Image
|
|
14
14
|
from torch.utils.data import DataLoader, Subset
|
|
15
15
|
from tqdm import tqdm
|
|
@@ -19,6 +19,7 @@ from euler_loading import MultiModalDataset
|
|
|
19
19
|
from .data import (
|
|
20
20
|
align_to_prediction,
|
|
21
21
|
classify_spatial_alignment,
|
|
22
|
+
compose_sensor_to_camera_extrinsics,
|
|
22
23
|
compute_scale_and_shift,
|
|
23
24
|
process_depth,
|
|
24
25
|
project_point_cloud_to_depth_map,
|
|
@@ -499,6 +500,15 @@ def _get_first_hierarchical_value(sample: dict, key: str):
|
|
|
499
500
|
return data
|
|
500
501
|
|
|
501
502
|
|
|
503
|
+
def _iter_hierarchical_values(sample: dict, key: str) -> list:
|
|
504
|
+
data = sample.get(key)
|
|
505
|
+
if data is None:
|
|
506
|
+
return []
|
|
507
|
+
if isinstance(data, dict):
|
|
508
|
+
return [data, *data.values()]
|
|
509
|
+
return [data]
|
|
510
|
+
|
|
511
|
+
|
|
502
512
|
def _get_intrinsics_K(sample: dict) -> Optional[np.ndarray]:
|
|
503
513
|
"""Extract (3,3) intrinsics matrix from sample calibration data."""
|
|
504
514
|
K_data = _get_first_hierarchical_value(sample, "intrinsics")
|
|
@@ -509,14 +519,197 @@ def _get_intrinsics_K(sample: dict) -> Optional[np.ndarray]:
|
|
|
509
519
|
return to_numpy_intrinsics(K_data)
|
|
510
520
|
|
|
511
521
|
|
|
512
|
-
|
|
513
|
-
""
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
522
|
+
_DIRECT_SOURCE_TO_CAMERA_KEYS = (
|
|
523
|
+
"lidar2rgb",
|
|
524
|
+
"lidar2camera",
|
|
525
|
+
"lidar2cam",
|
|
526
|
+
"lidar_to_rgb",
|
|
527
|
+
"lidar_to_camera",
|
|
528
|
+
"lidar_to_cam",
|
|
529
|
+
"source2rgb",
|
|
530
|
+
"source2camera",
|
|
531
|
+
"source_to_rgb",
|
|
532
|
+
"source_to_camera",
|
|
533
|
+
"sensor2rgb",
|
|
534
|
+
"sensor2camera",
|
|
535
|
+
"sensor_to_rgb",
|
|
536
|
+
"sensor_to_camera",
|
|
537
|
+
)
|
|
538
|
+
_SOURCE_SENSOR_POSE_KEYS = (
|
|
539
|
+
"lidar",
|
|
540
|
+
"lidar_pose",
|
|
541
|
+
"lidar_extrinsics",
|
|
542
|
+
"source",
|
|
543
|
+
"source_sensor",
|
|
544
|
+
"source_extrinsics",
|
|
545
|
+
"sensor",
|
|
546
|
+
"sensor_extrinsics",
|
|
547
|
+
"lidar2gnss",
|
|
548
|
+
"lidar2ego",
|
|
549
|
+
"lidar2vehicle",
|
|
550
|
+
"lidar2world",
|
|
551
|
+
"lidar_to_gnss",
|
|
552
|
+
"lidar_to_ego",
|
|
553
|
+
"lidar_to_vehicle",
|
|
554
|
+
"lidar_to_world",
|
|
555
|
+
)
|
|
556
|
+
_CAMERA_SENSOR_POSE_KEYS = (
|
|
557
|
+
"rgb",
|
|
558
|
+
"camera",
|
|
559
|
+
"cam",
|
|
560
|
+
"frame_camera",
|
|
561
|
+
"rgb_pose",
|
|
562
|
+
"camera_pose",
|
|
563
|
+
"camera_extrinsics",
|
|
564
|
+
"target",
|
|
565
|
+
"target_sensor",
|
|
566
|
+
"target_extrinsics",
|
|
567
|
+
"rgb2gnss",
|
|
568
|
+
"camera2gnss",
|
|
569
|
+
"cam2gnss",
|
|
570
|
+
"frame_camera2gnss",
|
|
571
|
+
"rgb2ego",
|
|
572
|
+
"camera2ego",
|
|
573
|
+
"cam2ego",
|
|
574
|
+
"frame_camera2ego",
|
|
575
|
+
"rgb2vehicle",
|
|
576
|
+
"camera2vehicle",
|
|
577
|
+
"cam2vehicle",
|
|
578
|
+
"frame_camera2vehicle",
|
|
579
|
+
"rgb2world",
|
|
580
|
+
"camera2world",
|
|
581
|
+
"cam2world",
|
|
582
|
+
"frame_camera2world",
|
|
583
|
+
)
|
|
584
|
+
_MATRIX_WRAPPER_KEYS = ("T", "transform", "matrix", "extrinsics", "pose")
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
def _as_extrinsics_or_none(data) -> Optional[np.ndarray]:
|
|
588
|
+
try:
|
|
589
|
+
return to_numpy_extrinsics(data)
|
|
590
|
+
except (TypeError, ValueError):
|
|
591
|
+
return None
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
def _extract_wrapped_extrinsics(data) -> Optional[np.ndarray]:
|
|
595
|
+
matrix = _as_extrinsics_or_none(data)
|
|
596
|
+
if matrix is not None:
|
|
597
|
+
return matrix
|
|
598
|
+
|
|
599
|
+
if not isinstance(data, Mapping):
|
|
600
|
+
return None
|
|
601
|
+
|
|
602
|
+
for key in _MATRIX_WRAPPER_KEYS:
|
|
603
|
+
if key in data:
|
|
604
|
+
matrix = _extract_wrapped_extrinsics(data[key])
|
|
605
|
+
if matrix is not None:
|
|
606
|
+
return matrix
|
|
607
|
+
|
|
608
|
+
if len(data) == 1:
|
|
609
|
+
return _extract_wrapped_extrinsics(next(iter(data.values())))
|
|
610
|
+
|
|
611
|
+
return None
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
def _extract_named_extrinsics(data, names: tuple[str, ...]) -> Optional[np.ndarray]:
|
|
615
|
+
if not isinstance(data, Mapping):
|
|
616
|
+
return _extract_wrapped_extrinsics(data)
|
|
617
|
+
|
|
618
|
+
for name in names:
|
|
619
|
+
if name in data:
|
|
620
|
+
matrix = _extract_wrapped_extrinsics(data[name])
|
|
621
|
+
if matrix is not None:
|
|
622
|
+
return matrix
|
|
623
|
+
|
|
624
|
+
for key in ("extrinsics", "sensors", "calibration", "poses"):
|
|
625
|
+
if key in data and isinstance(data[key], Mapping):
|
|
626
|
+
matrix = _extract_named_extrinsics(data[key], names)
|
|
627
|
+
if matrix is not None:
|
|
628
|
+
return matrix
|
|
629
|
+
|
|
630
|
+
if len(data) == 1:
|
|
631
|
+
return _extract_named_extrinsics(next(iter(data.values())), names)
|
|
632
|
+
|
|
633
|
+
return None
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
def _extract_direct_source_to_camera(data) -> Optional[np.ndarray]:
|
|
637
|
+
matrix = _as_extrinsics_or_none(data)
|
|
638
|
+
if matrix is not None:
|
|
639
|
+
return matrix
|
|
640
|
+
|
|
641
|
+
if not isinstance(data, Mapping):
|
|
518
642
|
return None
|
|
519
|
-
|
|
643
|
+
|
|
644
|
+
for key in _DIRECT_SOURCE_TO_CAMERA_KEYS:
|
|
645
|
+
if key in data:
|
|
646
|
+
matrix = _extract_wrapped_extrinsics(data[key])
|
|
647
|
+
if matrix is not None:
|
|
648
|
+
return matrix
|
|
649
|
+
|
|
650
|
+
for key in ("extrinsics", "calibration"):
|
|
651
|
+
if key in data and isinstance(data[key], Mapping):
|
|
652
|
+
matrix = _extract_direct_source_to_camera(data[key])
|
|
653
|
+
if matrix is not None:
|
|
654
|
+
return matrix
|
|
655
|
+
|
|
656
|
+
if len(data) == 1:
|
|
657
|
+
return _extract_direct_source_to_camera(next(iter(data.values())))
|
|
658
|
+
|
|
659
|
+
return None
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
def _get_pointcloud_to_camera_extrinsics(
|
|
663
|
+
sample: dict,
|
|
664
|
+
) -> tuple[Optional[np.ndarray], Optional[str]]:
|
|
665
|
+
"""Extract or compose the transform used for sparse point projection.
|
|
666
|
+
|
|
667
|
+
The common MUSES path supplies a direct ``lidar2rgb`` transform via
|
|
668
|
+
``camera_extrinsics``. Some datasets instead expose separate lidar and
|
|
669
|
+
camera sensor poses in a shared frame; in that case we compose them to the
|
|
670
|
+
same source-to-camera transform expected by the projector.
|
|
671
|
+
"""
|
|
672
|
+
lidar_pose = None
|
|
673
|
+
for value in _iter_hierarchical_values(sample, "lidar_extrinsics"):
|
|
674
|
+
lidar_pose = _extract_named_extrinsics(value, _SOURCE_SENSOR_POSE_KEYS)
|
|
675
|
+
if lidar_pose is not None:
|
|
676
|
+
break
|
|
677
|
+
|
|
678
|
+
camera_values = _iter_hierarchical_values(sample, "camera_extrinsics")
|
|
679
|
+
if not camera_values:
|
|
680
|
+
camera_values = _iter_hierarchical_values(sample, "extrinsics")
|
|
681
|
+
|
|
682
|
+
if lidar_pose is not None:
|
|
683
|
+
for value in camera_values:
|
|
684
|
+
camera_pose = _extract_named_extrinsics(value, _CAMERA_SENSOR_POSE_KEYS)
|
|
685
|
+
if camera_pose is not None:
|
|
686
|
+
return (
|
|
687
|
+
compose_sensor_to_camera_extrinsics(lidar_pose, camera_pose),
|
|
688
|
+
"composed_lidar_and_camera_sensor_poses",
|
|
689
|
+
)
|
|
690
|
+
return None, None
|
|
691
|
+
|
|
692
|
+
for value in camera_values:
|
|
693
|
+
direct = _extract_direct_source_to_camera(value)
|
|
694
|
+
if direct is not None:
|
|
695
|
+
return direct, "direct_source_to_camera"
|
|
696
|
+
|
|
697
|
+
for value in camera_values:
|
|
698
|
+
lidar_pose = _extract_named_extrinsics(value, _SOURCE_SENSOR_POSE_KEYS)
|
|
699
|
+
camera_pose = _extract_named_extrinsics(value, _CAMERA_SENSOR_POSE_KEYS)
|
|
700
|
+
if lidar_pose is not None and camera_pose is not None:
|
|
701
|
+
return (
|
|
702
|
+
compose_sensor_to_camera_extrinsics(lidar_pose, camera_pose),
|
|
703
|
+
"composed_lidar_and_camera_sensor_poses",
|
|
704
|
+
)
|
|
705
|
+
|
|
706
|
+
return None, None
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
def _get_camera_extrinsics(sample: dict) -> Optional[np.ndarray]:
|
|
710
|
+
"""Backward-compatible wrapper for direct sparse projection callers."""
|
|
711
|
+
transform, _ = _get_pointcloud_to_camera_extrinsics(sample)
|
|
712
|
+
return transform
|
|
520
713
|
|
|
521
714
|
|
|
522
715
|
# ---------------------------------------------------------------------------
|
|
@@ -1658,6 +1851,7 @@ def evaluate_sparse_depth_samples(
|
|
|
1658
1851
|
total_input_points = 0
|
|
1659
1852
|
total_projected_pixels = 0
|
|
1660
1853
|
total_evaluated_pixels = 0
|
|
1854
|
+
projection_extrinsics_source: Optional[str] = None
|
|
1661
1855
|
|
|
1662
1856
|
if alignment_mode == "none":
|
|
1663
1857
|
print("Sparse depth alignment mode: none")
|
|
@@ -1711,15 +1905,22 @@ def evaluate_sparse_depth_samples(
|
|
|
1711
1905
|
point_cloud = to_numpy_point_cloud(sample["gt"])
|
|
1712
1906
|
depth_pred = to_numpy_depth(sample["pred"])
|
|
1713
1907
|
intrinsics_K = _get_intrinsics_K(sample)
|
|
1714
|
-
camera_extrinsics =
|
|
1908
|
+
camera_extrinsics, extrinsics_source = (
|
|
1909
|
+
_get_pointcloud_to_camera_extrinsics(sample)
|
|
1910
|
+
)
|
|
1715
1911
|
if intrinsics_K is None:
|
|
1716
1912
|
raise ValueError(
|
|
1717
1913
|
f"Sample {entry_id!r} is missing intrinsics for sparse depth projection."
|
|
1718
1914
|
)
|
|
1719
1915
|
if camera_extrinsics is None:
|
|
1720
1916
|
raise ValueError(
|
|
1721
|
-
f"Sample {entry_id!r} is missing camera_extrinsics
|
|
1917
|
+
f"Sample {entry_id!r} is missing usable camera_extrinsics "
|
|
1918
|
+
"for sparse depth projection. Provide a direct "
|
|
1919
|
+
"source-to-camera transform (for MUSES, lidar2rgb) or "
|
|
1920
|
+
"separate lidar_extrinsics and camera_extrinsics sensor poses."
|
|
1722
1921
|
)
|
|
1922
|
+
if projection_extrinsics_source is None:
|
|
1923
|
+
projection_extrinsics_source = extrinsics_source
|
|
1723
1924
|
|
|
1724
1925
|
if i == 0:
|
|
1725
1926
|
pred_native_dims = depth_pred.shape[:2]
|
|
@@ -2015,6 +2216,7 @@ def evaluate_sparse_depth_samples(
|
|
|
2015
2216
|
if pred_native_dims
|
|
2016
2217
|
else None,
|
|
2017
2218
|
"method": "pointcloud_projection",
|
|
2219
|
+
"extrinsics_source": projection_extrinsics_source,
|
|
2018
2220
|
"evaluated_dimensions": {
|
|
2019
2221
|
"height": pred_native_dims[0],
|
|
2020
2222
|
"width": pred_native_dims[1],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: euler-eval
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.13.0
|
|
4
4
|
Summary: Depth map evaluation toolkit with comprehensive metrics
|
|
5
5
|
Author: Depth Eval Contributors
|
|
6
6
|
License: MIT
|
|
@@ -149,7 +149,7 @@ This pre-downloads:
|
|
|
149
149
|
# Evaluate with default settings (auto-selects CUDA when available)
|
|
150
150
|
depth-eval config.json --batch-size 32
|
|
151
151
|
|
|
152
|
-
# Evaluate with sky masking enabled (requires gt.segmentation
|
|
152
|
+
# Evaluate with sky masking enabled (requires gt.segmentation or gt.semantic_segmentation)
|
|
153
153
|
depth-eval config.json --mask-sky -v
|
|
154
154
|
|
|
155
155
|
# Skip RGB evaluation, only evaluate depth
|
|
@@ -241,7 +241,13 @@ Defines GT modalities, prediction datasets to evaluate, and optional euler_train
|
|
|
241
241
|
|
|
242
242
|
Each modality entry can optionally include a `split` field to select a specific split from the dataset (e.g. `{ "path": "/data/gt/depth", "split": "test" }`). euler-loading inline selectors are also accepted in `path`, such as `/data/muses.zip:test` or `/data/muses.zip:test#scope=rgb`.
|
|
243
243
|
|
|
244
|
-
For sparse pointcloud depth evaluation, use `gt.sparse_depth` instead of `gt.depth`. The prediction uses a dense depth-like map under `datasets[].depth`, `datasets[].relative_depth`, or `datasets[].affine_depth`. The evaluator projects the sparse GT point cloud into the prediction image plane using `gt.intrinsics` and `gt.camera_extrinsics`, then computes pointwise depth metrics only at projected valid pixels.
|
|
244
|
+
For sparse pointcloud depth evaluation, use `gt.sparse_depth` instead of `gt.depth`. The prediction uses a dense depth-like map under `datasets[].depth`, `datasets[].relative_depth`, or `datasets[].affine_depth`. The evaluator projects the sparse GT point cloud into the prediction image plane using `gt.intrinsics` and `gt.camera_extrinsics`, then computes pointwise depth metrics only at projected valid pixels. For MUSES through `euler_loading.loaders.muses`, `gt.camera_extrinsics` normally resolves to the direct `lidar2rgb` transform and no extra lidar pose is needed. If a dataset exposes separate lidar and camera poses in a shared frame instead, provide optional `gt.lidar_extrinsics`; the evaluator composes `inv(camera_pose) @ lidar_pose` before projection.
|
|
245
|
+
|
|
246
|
+
Sparse depth does not require segmentation GT. `gt.segmentation` and
|
|
247
|
+
`gt.semantic_segmentation` are optional aliases for the same sky-mask source.
|
|
248
|
+
They are loaded only when `--mask-sky` is set; in sparse depth evaluation that
|
|
249
|
+
mask excludes sky pixels from projected-point metrics and from scale/shift
|
|
250
|
+
fitting. Without `--mask-sky`, the segmentation entry is not loaded or used.
|
|
245
251
|
|
|
246
252
|
#### GT section
|
|
247
253
|
|
|
@@ -251,10 +257,11 @@ For sparse pointcloud depth evaluation, use `gt.sparse_depth` instead of `gt.dep
|
|
|
251
257
|
| `gt.depth.path` | no\* | Path to GT depth dataset |
|
|
252
258
|
| `gt.sparse_depth.path` | no\* | Path to sparse pointcloud GT dataset, e.g. `sparse_depth` with `(N,C)` points whose first columns are `x,y,z` in meters |
|
|
253
259
|
| `gt.rays.path` | no\* | Path to GT ray direction map dataset (for rays evaluation) |
|
|
254
|
-
| `gt.segmentation.path` | no | Path to GT segmentation (needed for `--mask-sky
|
|
260
|
+
| `gt.segmentation.path` / `gt.semantic_segmentation.path` | no | Path to GT segmentation (needed for `--mask-sky`; use one key, not both) |
|
|
255
261
|
| `gt.calibration.path` | no | Path to calibration data (camera intrinsics matrices) |
|
|
256
262
|
| `gt.intrinsics.path` | required with `gt.sparse_depth` | Path to camera intrinsics matrices for pointcloud projection |
|
|
257
263
|
| `gt.camera_extrinsics.path` | required with `gt.sparse_depth` | Path to source-to-camera extrinsics, e.g. MUSES `lidar2rgb`, for pointcloud projection |
|
|
264
|
+
| `gt.lidar_extrinsics.path` | no | Optional lidar sensor pose in the same shared frame as `gt.camera_extrinsics`; when provided, `gt.camera_extrinsics` is interpreted as the camera pose and both are composed for projection |
|
|
258
265
|
| `gt.name` | no | Display name for ground truth (default: `"GT"`) |
|
|
259
266
|
|
|
260
267
|
\* At least one of `gt.rgb.path`, `gt.depth.path`, `gt.sparse_depth.path`, or `gt.rays.path` is required.
|
|
@@ -4,7 +4,12 @@ import json
|
|
|
4
4
|
import zipfile
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
|
-
from euler_eval.cli import
|
|
7
|
+
from euler_eval.cli import (
|
|
8
|
+
_gt_segmentation_entry,
|
|
9
|
+
load_config,
|
|
10
|
+
validate_dataset_entry,
|
|
11
|
+
validate_gt_config,
|
|
12
|
+
)
|
|
8
13
|
|
|
9
14
|
|
|
10
15
|
# ---------------------------------------------------------------------------
|
|
@@ -39,6 +44,36 @@ class TestValidateGtConfig:
|
|
|
39
44
|
}
|
|
40
45
|
validate_gt_config(gt)
|
|
41
46
|
|
|
47
|
+
def test_valid_with_semantic_segmentation_alias(self, tmp_path):
|
|
48
|
+
"""GT config accepts semantic_segmentation as a segmentation alias."""
|
|
49
|
+
for name in ("depth", "semantic_segmentation"):
|
|
50
|
+
(tmp_path / name).mkdir()
|
|
51
|
+
|
|
52
|
+
gt = {
|
|
53
|
+
"depth": {"path": str(tmp_path / "depth")},
|
|
54
|
+
"semantic_segmentation": {
|
|
55
|
+
"path": str(tmp_path / "semantic_segmentation")
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
validate_gt_config(gt)
|
|
59
|
+
key, entry = _gt_segmentation_entry(gt)
|
|
60
|
+
assert key == "semantic_segmentation"
|
|
61
|
+
assert entry == gt["semantic_segmentation"]
|
|
62
|
+
|
|
63
|
+
def test_rejects_duplicate_segmentation_aliases(self, tmp_path):
|
|
64
|
+
for name in ("depth", "segmentation", "semantic_segmentation"):
|
|
65
|
+
(tmp_path / name).mkdir()
|
|
66
|
+
|
|
67
|
+
gt = {
|
|
68
|
+
"depth": {"path": str(tmp_path / "depth")},
|
|
69
|
+
"segmentation": {"path": str(tmp_path / "segmentation")},
|
|
70
|
+
"semantic_segmentation": {
|
|
71
|
+
"path": str(tmp_path / "semantic_segmentation")
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
with pytest.raises(ValueError, match="multiple segmentation entries"):
|
|
75
|
+
validate_gt_config(gt)
|
|
76
|
+
|
|
42
77
|
def test_depth_only_gt_is_valid(self, tmp_path):
|
|
43
78
|
depth_path = tmp_path / "depth"
|
|
44
79
|
depth_path.mkdir()
|
|
@@ -101,6 +136,22 @@ class TestValidateGtConfig:
|
|
|
101
136
|
}
|
|
102
137
|
validate_gt_config(gt)
|
|
103
138
|
|
|
139
|
+
def test_sparse_depth_gt_accepts_optional_lidar_extrinsics(self, tmp_path):
|
|
140
|
+
for name in (
|
|
141
|
+
"sparse_depth",
|
|
142
|
+
"intrinsics",
|
|
143
|
+
"camera_extrinsics",
|
|
144
|
+
"lidar_extrinsics",
|
|
145
|
+
):
|
|
146
|
+
(tmp_path / name).mkdir()
|
|
147
|
+
gt = {
|
|
148
|
+
"sparse_depth": {"path": str(tmp_path / "sparse_depth")},
|
|
149
|
+
"intrinsics": {"path": str(tmp_path / "intrinsics")},
|
|
150
|
+
"camera_extrinsics": {"path": str(tmp_path / "camera_extrinsics")},
|
|
151
|
+
"lidar_extrinsics": {"path": str(tmp_path / "lidar_extrinsics")},
|
|
152
|
+
}
|
|
153
|
+
validate_gt_config(gt)
|
|
154
|
+
|
|
104
155
|
def test_nonexistent_path(self, tmp_path):
|
|
105
156
|
rgb_path = tmp_path / "rgb"
|
|
106
157
|
rgb_path.mkdir()
|
|
@@ -35,7 +35,7 @@ def test_depth_builder_sets_modality_scopes(monkeypatch):
|
|
|
35
35
|
monkeypatch.setattr(
|
|
36
36
|
data,
|
|
37
37
|
"_resolve_sky_mask_loader",
|
|
38
|
-
lambda path: sky_loader,
|
|
38
|
+
lambda path, *, modality_key="segmentation": sky_loader,
|
|
39
39
|
)
|
|
40
40
|
|
|
41
41
|
dataset = data.build_depth_eval_dataset(
|
|
@@ -100,6 +100,27 @@ def test_sparse_depth_builder_sets_projection_modality_scopes(monkeypatch):
|
|
|
100
100
|
key="camera_extrinsics",
|
|
101
101
|
split="pose",
|
|
102
102
|
)
|
|
103
|
+
assert "segmentation" not in dataset.hierarchical_modalities
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def test_sparse_depth_builder_adds_optional_lidar_extrinsics(monkeypatch):
|
|
107
|
+
_install_captured_dataset(monkeypatch)
|
|
108
|
+
|
|
109
|
+
dataset = data.build_sparse_depth_eval_dataset(
|
|
110
|
+
gt_sparse_depth_path="/datasets/shared",
|
|
111
|
+
pred_depth_path="/predictions/shared",
|
|
112
|
+
intrinsics_path="/datasets/shared",
|
|
113
|
+
camera_extrinsics_path="/datasets/shared",
|
|
114
|
+
lidar_extrinsics_path="/datasets/shared",
|
|
115
|
+
lidar_extrinsics_split="pose",
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
_assert_modality(
|
|
119
|
+
dataset.hierarchical_modalities["lidar_extrinsics"],
|
|
120
|
+
key="camera_extrinsics",
|
|
121
|
+
split="pose",
|
|
122
|
+
scope="lidar_extrinsics",
|
|
123
|
+
)
|
|
103
124
|
|
|
104
125
|
|
|
105
126
|
def test_sparse_depth_builder_can_load_relative_depth_prediction_scope(monkeypatch):
|
|
@@ -121,6 +142,40 @@ def test_sparse_depth_builder_can_load_relative_depth_prediction_scope(monkeypat
|
|
|
121
142
|
)
|
|
122
143
|
|
|
123
144
|
|
|
145
|
+
def test_sparse_depth_builder_uses_semantic_segmentation_scope(monkeypatch):
|
|
146
|
+
_install_captured_dataset(monkeypatch)
|
|
147
|
+
calls = []
|
|
148
|
+
|
|
149
|
+
def sky_loader(path, meta=None):
|
|
150
|
+
return None
|
|
151
|
+
|
|
152
|
+
def resolve_sky_mask_loader(path, *, modality_key="segmentation"):
|
|
153
|
+
calls.append((path, modality_key))
|
|
154
|
+
return sky_loader
|
|
155
|
+
|
|
156
|
+
monkeypatch.setattr(
|
|
157
|
+
data,
|
|
158
|
+
"_resolve_sky_mask_loader",
|
|
159
|
+
resolve_sky_mask_loader,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
dataset = data.build_sparse_depth_eval_dataset(
|
|
163
|
+
gt_sparse_depth_path="/datasets/shared",
|
|
164
|
+
pred_depth_path="/predictions/shared",
|
|
165
|
+
intrinsics_path="/datasets/shared",
|
|
166
|
+
camera_extrinsics_path="/datasets/shared",
|
|
167
|
+
segmentation_path="/datasets/shared",
|
|
168
|
+
segmentation_modality_key="semantic_segmentation",
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
assert calls == [("/datasets/shared", "semantic_segmentation")]
|
|
172
|
+
_assert_modality(
|
|
173
|
+
dataset.hierarchical_modalities["segmentation"],
|
|
174
|
+
key="semantic_segmentation",
|
|
175
|
+
loader=sky_loader,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
|
|
124
179
|
def test_rgb_builder_sets_rgb_and_auxiliary_modality_scopes(monkeypatch):
|
|
125
180
|
_install_captured_dataset(monkeypatch)
|
|
126
181
|
|
|
@@ -215,6 +270,35 @@ def test_sky_mask_loader_resolution_strips_inline_split(monkeypatch):
|
|
|
215
270
|
]
|
|
216
271
|
|
|
217
272
|
|
|
273
|
+
def test_sky_mask_loader_resolution_accepts_semantic_segmentation_scope(monkeypatch):
|
|
274
|
+
calls = []
|
|
275
|
+
|
|
276
|
+
def fake_index_dataset_from_path(path, **kwargs):
|
|
277
|
+
calls.append((path, kwargs))
|
|
278
|
+
return {"euler_loading": {"loader": "muses"}}
|
|
279
|
+
|
|
280
|
+
def sky_mask(path, meta=None):
|
|
281
|
+
return None
|
|
282
|
+
|
|
283
|
+
monkeypatch.setattr(data, "index_dataset_from_path", fake_index_dataset_from_path)
|
|
284
|
+
monkeypatch.setattr(
|
|
285
|
+
data,
|
|
286
|
+
"resolve_loader_module",
|
|
287
|
+
lambda name: SimpleNamespace(sky_mask=sky_mask),
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
assert (
|
|
291
|
+
data._resolve_sky_mask_loader(
|
|
292
|
+
"/datasets/shared",
|
|
293
|
+
modality_key="semantic_segmentation",
|
|
294
|
+
)
|
|
295
|
+
is sky_mask
|
|
296
|
+
)
|
|
297
|
+
assert calls == [
|
|
298
|
+
("/datasets/shared", {"metadata_scope": "semantic_segmentation"}),
|
|
299
|
+
]
|
|
300
|
+
|
|
301
|
+
|
|
218
302
|
def test_modality_parses_inline_split_and_scope():
|
|
219
303
|
modality = data._modality(
|
|
220
304
|
path="/datasets/frame_camera_trainvaltest.zip:fog_day#scope=intrinsics",
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import numpy as np
|
|
4
4
|
|
|
5
5
|
from euler_eval.data import (
|
|
6
|
+
compose_sensor_to_camera_extrinsics,
|
|
6
7
|
project_point_cloud_to_depth_map,
|
|
7
8
|
to_numpy_extrinsics,
|
|
8
9
|
to_numpy_point_cloud,
|
|
@@ -29,6 +30,22 @@ def test_to_numpy_extrinsics_expands_3x4_matrix():
|
|
|
29
30
|
np.testing.assert_array_equal(result[3], np.array([0.0, 0.0, 0.0, 1.0]))
|
|
30
31
|
|
|
31
32
|
|
|
33
|
+
def test_compose_sensor_to_camera_extrinsics_uses_lidar_and_camera_poses():
|
|
34
|
+
lidar_to_world = np.eye(4, dtype=np.float32)
|
|
35
|
+
lidar_to_world[0, 3] = 12.0
|
|
36
|
+
camera_to_world = np.eye(4, dtype=np.float32)
|
|
37
|
+
camera_to_world[0, 3] = 10.0
|
|
38
|
+
|
|
39
|
+
result = compose_sensor_to_camera_extrinsics(
|
|
40
|
+
lidar_to_world,
|
|
41
|
+
camera_to_world,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
expected = np.eye(4, dtype=np.float32)
|
|
45
|
+
expected[0, 3] = 2.0
|
|
46
|
+
np.testing.assert_allclose(result, expected)
|
|
47
|
+
|
|
48
|
+
|
|
32
49
|
def test_project_point_cloud_to_depth_map_uses_nearest_point_per_pixel():
|
|
33
50
|
intrinsics = np.eye(3, dtype=np.float32)
|
|
34
51
|
extrinsics = np.eye(4, dtype=np.float32)
|
|
@@ -115,6 +132,52 @@ class _AffineSparseDataset(_OneSampleSparseDataset):
|
|
|
115
132
|
self.pred[1, 2] = np.sqrt(24.0) * 10.0 + 7.0
|
|
116
133
|
|
|
117
134
|
|
|
135
|
+
class _SeparateSensorPoseSparseDataset(_OneSampleSparseDataset):
|
|
136
|
+
def __init__(self):
|
|
137
|
+
super().__init__()
|
|
138
|
+
self.point_cloud = np.array([[0.0, 1.0, 1.0, 0.0]], dtype=np.float32)
|
|
139
|
+
self.camera_extrinsics = np.eye(4, dtype=np.float32)
|
|
140
|
+
self.camera_extrinsics[0, 3] = 10.0
|
|
141
|
+
self.lidar_extrinsics = np.eye(4, dtype=np.float32)
|
|
142
|
+
self.lidar_extrinsics[0, 3] = 12.0
|
|
143
|
+
self.pred = np.zeros((3, 3), dtype=np.float32)
|
|
144
|
+
self.pred[1, 2] = np.sqrt(6.0)
|
|
145
|
+
|
|
146
|
+
def __getitem__(self, index):
|
|
147
|
+
if index != 0:
|
|
148
|
+
raise IndexError(index)
|
|
149
|
+
return {
|
|
150
|
+
"id": "000000",
|
|
151
|
+
"full_id": "/000000",
|
|
152
|
+
"gt": self.point_cloud,
|
|
153
|
+
"pred": self.pred,
|
|
154
|
+
"intrinsics": {"intrinsics": self.intrinsics},
|
|
155
|
+
"camera_extrinsics": {"camera_pose": self.camera_extrinsics},
|
|
156
|
+
"lidar_extrinsics": {"lidar_pose": self.lidar_extrinsics},
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class _SharedCalibrationSparseDataset(_SeparateSensorPoseSparseDataset):
|
|
161
|
+
def __getitem__(self, index):
|
|
162
|
+
if index != 0:
|
|
163
|
+
raise IndexError(index)
|
|
164
|
+
return {
|
|
165
|
+
"id": "000000",
|
|
166
|
+
"full_id": "/000000",
|
|
167
|
+
"gt": self.point_cloud,
|
|
168
|
+
"pred": self.pred,
|
|
169
|
+
"intrinsics": {"intrinsics": self.intrinsics},
|
|
170
|
+
"camera_extrinsics": {
|
|
171
|
+
"calib": {
|
|
172
|
+
"extrinsics": {
|
|
173
|
+
"lidar2ego": self.lidar_extrinsics,
|
|
174
|
+
"rgb2ego": self.camera_extrinsics,
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
|
|
118
181
|
def test_sparse_depth_eval_reports_only_pointwise_depth_metrics():
|
|
119
182
|
dataset = _OneSampleSparseDataset()
|
|
120
183
|
|
|
@@ -135,6 +198,47 @@ def test_sparse_depth_eval_reports_only_pointwise_depth_metrics():
|
|
|
135
198
|
np.testing.assert_allclose(metrics["depth_metrics"]["rmse"]["median"], 0.0)
|
|
136
199
|
|
|
137
200
|
|
|
201
|
+
def test_sparse_depth_eval_composes_separate_lidar_and_camera_poses():
|
|
202
|
+
result = evaluate_sparse_depth_samples(
|
|
203
|
+
_SeparateSensorPoseSparseDataset(),
|
|
204
|
+
pred_is_radial=True,
|
|
205
|
+
num_workers=0,
|
|
206
|
+
alignment_mode="none",
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
assert result["dataset_info"]["projected_pixels"] == 1
|
|
210
|
+
assert result["dataset_info"]["evaluated_pixels"] == 1
|
|
211
|
+
assert (
|
|
212
|
+
result["spatial_info"]["extrinsics_source"]
|
|
213
|
+
== "composed_lidar_and_camera_sensor_poses"
|
|
214
|
+
)
|
|
215
|
+
np.testing.assert_allclose(
|
|
216
|
+
result["sparse_depth_metric"]["depth_metrics"]["rmse"]["median"],
|
|
217
|
+
0.0,
|
|
218
|
+
atol=1e-6,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def test_sparse_depth_eval_composes_sensor_poses_from_shared_calibration():
|
|
223
|
+
result = evaluate_sparse_depth_samples(
|
|
224
|
+
_SharedCalibrationSparseDataset(),
|
|
225
|
+
pred_is_radial=True,
|
|
226
|
+
num_workers=0,
|
|
227
|
+
alignment_mode="none",
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
assert result["dataset_info"]["projected_pixels"] == 1
|
|
231
|
+
assert (
|
|
232
|
+
result["spatial_info"]["extrinsics_source"]
|
|
233
|
+
== "composed_lidar_and_camera_sensor_poses"
|
|
234
|
+
)
|
|
235
|
+
np.testing.assert_allclose(
|
|
236
|
+
result["sparse_depth_metric"]["depth_metrics"]["rmse"]["median"],
|
|
237
|
+
0.0,
|
|
238
|
+
atol=1e-6,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
|
|
138
242
|
def test_sparse_depth_eval_auto_affine_aligns_relative_depth():
|
|
139
243
|
result = evaluate_sparse_depth_samples(
|
|
140
244
|
_RelativeSparseDataset(),
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|