euler-eval 2.35.0__tar.gz → 2.36.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.35.0/euler_eval.egg-info → euler_eval-2.36.0}/PKG-INFO +1 -1
- {euler_eval-2.35.0 → euler_eval-2.36.0}/docs/cli.md +4 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/docs/configuration.md +11 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/docs/output.md +20 -6
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/cli.py +66 -2
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/data.py +54 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/evaluate.py +17 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/protocol.py +1 -1
- {euler_eval-2.35.0 → euler_eval-2.36.0/euler_eval.egg-info}/PKG-INFO +1 -1
- {euler_eval-2.35.0 → euler_eval-2.36.0}/pyproject.toml +1 -1
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_mask_sky_pairing.py +127 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_protocol.py +12 -1
- {euler_eval-2.35.0 → euler_eval-2.36.0}/LICENSE +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/MANIFEST.in +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/README.md +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/docs/README.md +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/docs/alignment.md +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/docs/distributions.md +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/docs/feature-request-unified-evaluation-framework.md +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/docs/metrics.md +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/docs/validation.md +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/THIRD_PARTY_NOTICES.md +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/__init__.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/calibration.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/config_paths.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/distribution_contract.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/distributions.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metric_sets.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/__init__.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/_natural_scene_models.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/absrel.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/daniel_error.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/depth_binned_error.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/depth_edge_f1.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/depth_standard.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/fade.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/fid_kid.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/gpu_depth_batch.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/gpu_image_batch.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/high_freq_energy.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/lpips_metric.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/niqe.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/normal_consistency.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/points3d_cloud.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/points3d_decomposition.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/points3d_distance.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/points3d_geometry.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/psnr.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/rgb_edge_f1.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/rgb_lpips.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/rgb_psnr_ssim.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/rho_a.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/rmse.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/scale_invariant_log.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/sky_iou.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/ssim.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/tail_errors.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/metrics/utils.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/sanity_checker.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/schemas/distribution-v1.schema.json +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/utils/hierarchy_parser.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval/validation.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval.egg-info/SOURCES.txt +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval.egg-info/dependency_links.txt +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval.egg-info/entry_points.txt +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval.egg-info/requires.txt +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/euler_eval.egg-info/top_level.txt +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/init_cache.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/scripts/generate_distribution_fixtures.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/setup.cfg +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/__init__.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/conftest.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/distribution_contract_utils.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/eval_output.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/fixtures/distributions/README.md +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/fixtures/distributions/counts-depth.eval.json +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/fixtures/distributions/counts-points3d.eval.json +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/fixtures/distributions/counts-sparsedepth.eval.json +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/fixtures/distributions/future-sum-by-depth.eval.json +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/fixtures/distributions/histogram-only.eval.json +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/fixtures/distributions/per-file-only.eval.json +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/fixtures/distributions/scalar-only.eval.json +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/mock_files/calibration_output.json +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/mock_files/depth_output.json +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/mock_files/rgb_output.json +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/mock_files/segmentation_output.json +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_alignment.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_cli_device.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_cli_sparse_outputs.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_config.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_data.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_data_builders.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_depth_alignment_output.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_depth_standard.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_distribution_contract.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_distributions.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_domain_metrics.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_evaluate_helpers.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_experiments.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_init_cache.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_integration.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_mask_sky_prediction.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_meta_output.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_metrics_utils.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_normal_consistency.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_points_3d.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_points_3d_sparse.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_rgb_fid_output.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_rho_a.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_save_results.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_sky_iou.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_sky_iou_cli.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_sparse_depth.py +0 -0
- {euler_eval-2.35.0 → euler_eval-2.36.0}/tests/test_validation.py +0 -0
|
@@ -23,6 +23,7 @@ validation exits with status `1` before any dataset is opened.
|
|
|
23
23
|
| `--skip-rays` | flag | off | Skip rays (spherical direction map) evaluation |
|
|
24
24
|
| `--skip-points-3d` | flag | off | Skip points_3d (per-pixel 3D point map) evaluation |
|
|
25
25
|
| `--mask-sky` | flag | off | Mask sky regions from metrics using GT segmentation |
|
|
26
|
+
| `--missing-segmentation` | `{drop,unmasked}` | `drop` | Frames without their own GT segmentation: `drop` leaves them out; `unmasked` evaluates them without GT sky masking and leaves them out of the sky IoU ([details](configuration.md#gt-segmentation)) |
|
|
26
27
|
| `--mask-sky-prediction` | flag | off | Exclude pixels in a prediction's `datasets[].sky_mask` from dense and sparse depth metrics and the alignment fit; predictions without a `sky_mask` are scored in full ([details](configuration.md#masking-predicted-sky)) |
|
|
27
28
|
| `--sky-depth` | `float` | none | Cap GT and predicted depths in meters and fill predicted `sky_mask` pixels with this value; must be finite and positive |
|
|
28
29
|
| `--sky-iou` | flag | off | Score predicted sky against GT segmentation sky: IoU, precision and recall for the `explicit` (`datasets[].sky_mask`) and `implicit` (`depth >= --sky-depth`, metric predictions only) variants. Requires `gt.segmentation`; depth metrics are unchanged ([details](metrics.md#sky-segmentation)) |
|
|
@@ -58,6 +59,9 @@ euler-eval config.json --batch-size 32
|
|
|
58
59
|
# Sky masking (requires gt.segmentation)
|
|
59
60
|
euler-eval config.json --mask-sky -v
|
|
60
61
|
|
|
62
|
+
# Keep frames that have no GT segmentation, evaluated without sky masking
|
|
63
|
+
euler-eval config.json --mask-sky --missing-segmentation unmasked
|
|
64
|
+
|
|
61
65
|
# Exclude each prediction's own sky (datasets[].sky_mask) from depth metrics
|
|
62
66
|
euler-eval config.json --mask-sky-prediction
|
|
63
67
|
|
|
@@ -103,6 +103,17 @@ warns and the sky IoU is skipped. A segmentation whose IDs match no frame
|
|
|
103
103
|
stops the run with euler-loading's "No common IDs" error and per-modality file
|
|
104
104
|
counts.
|
|
105
105
|
|
|
106
|
+
When a split has segmentation for only some frames (MUSES lacks some), pass
|
|
107
|
+
`--missing-segmentation unmasked` to keep the other frames: they are evaluated
|
|
108
|
+
without GT sky masking and left out of the sky IoU, while frames with
|
|
109
|
+
segmentation are scored exactly as before. The metrics then mix masked and
|
|
110
|
+
unmasked frames, so `meta.gt_sky_masking` records how many frames had no
|
|
111
|
+
segmentation, and `protocol.parameters` records the policy
|
|
112
|
+
(`population.missing_segmentation`). Unmasked frames score their sky wherever
|
|
113
|
+
the GT has valid depth there: little for a lidar GT, which has few returns in
|
|
114
|
+
the sky, but possibly a lot for a dense GT with sky depth. The default,
|
|
115
|
+
`drop`, leaves those frames out.
|
|
116
|
+
|
|
106
117
|
The config key is also the default metadata scope. If the path has no
|
|
107
118
|
`#scope=` selector, no root-level metadata, and no scope of that name, the
|
|
108
119
|
scope is chosen among the archive's scopes: the one whose name or
|
|
@@ -74,7 +74,7 @@ set.
|
|
|
74
74
|
"metricDescriptions": { "absrel": { "…": "…" } }
|
|
75
75
|
},
|
|
76
76
|
"protocol": {
|
|
77
|
-
"schemaVersion": "1.
|
|
77
|
+
"schemaVersion": "1.2",
|
|
78
78
|
"parameters": { "reference.gt.dataset": "muses", "method.depth_alignment": "auto_affine", "…": "…" }
|
|
79
79
|
},
|
|
80
80
|
"dataset_info": { "num_pairs": 500, "gt_name": "GT", "pred_name": "model_a" },
|
|
@@ -171,6 +171,19 @@ sky removed, since that differs from model to model:
|
|
|
171
171
|
depth) that would be scored without the flag, after `--mask-sky` if set;
|
|
172
172
|
`excluded_gt_pixels` is how many of them fell in predicted sky.
|
|
173
173
|
|
|
174
|
+
### GT sky masking coverage
|
|
175
|
+
|
|
176
|
+
With `--mask-sky`, every modality it applies to records how many frames had
|
|
177
|
+
their own GT segmentation:
|
|
178
|
+
|
|
179
|
+
```json
|
|
180
|
+
"gt_sky_masking": { "samples": 500, "samples_without_segmentation": 100, "samples_with_sky": 396 }
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
`samples_without_segmentation` is `0` unless `--missing-segmentation unmasked`
|
|
184
|
+
kept frames without one; those frames were scored without sky masking.
|
|
185
|
+
`samples_with_sky` counts the frames whose mask removed any pixel.
|
|
186
|
+
|
|
174
187
|
When sky keys are written, the metric-set axes also declare them:
|
|
175
188
|
`sky_segmentation` joins the `category` values, an optional `variant` axis
|
|
176
189
|
(`explicit`, `implicit`) follows `category`, and `space` becomes optional
|
|
@@ -188,7 +201,7 @@ values differ say where. Nothing else in the file needs to be compared for that;
|
|
|
188
201
|
|
|
189
202
|
```json
|
|
190
203
|
"protocol": {
|
|
191
|
-
"schemaVersion": "1.
|
|
204
|
+
"schemaVersion": "1.2",
|
|
192
205
|
"parameters": {
|
|
193
206
|
"reference.gt.modality": "sparse_depth",
|
|
194
207
|
"reference.gt.dataset": "seeingthroughfog",
|
|
@@ -208,6 +221,7 @@ values differ say where. Nothing else in the file needs to be compared for that;
|
|
|
208
221
|
"reference.segmentation.split": null,
|
|
209
222
|
"population.samples": 943,
|
|
210
223
|
"population.gt_sky_masking": false,
|
|
224
|
+
"population.missing_segmentation": null,
|
|
211
225
|
"population.pred_sky_masking": false,
|
|
212
226
|
"population.evaluated_resolution": [1024, 1920],
|
|
213
227
|
"population.benchmark_depth_range_m": [0.01, 80.0],
|
|
@@ -231,12 +245,12 @@ What is recorded, per modality:
|
|
|
231
245
|
|
|
232
246
|
| Modality | `reference` inputs besides `gt` | `population` | `method` |
|
|
233
247
|
|---|---|---|---|
|
|
234
|
-
| `depth` | `calibration`, `segmentation` | `samples`, `gt_sky_masking`, `pred_sky_masking`, `evaluated_resolution`, `benchmark_depth_range_m` | `depth_alignment`, `sky_depth_m`, `pred_sky_mask`, `spatial_alignment` |
|
|
248
|
+
| `depth` | `calibration`, `segmentation` | `samples`, `gt_sky_masking`, `missing_segmentation`, `pred_sky_masking`, `evaluated_resolution`, `benchmark_depth_range_m` | `depth_alignment`, `sky_depth_m`, `pred_sky_mask`, `spatial_alignment` |
|
|
235
249
|
| `sparsedepth` | `intrinsics`, `camera_extrinsics`, `lidar_extrinsics`, `segmentation` | as `depth` | `depth_alignment`, `sky_depth_m`, `pred_sky_mask` |
|
|
236
|
-
| `rgb` | `depth`, `calibration`, `segmentation` | `samples`, `gt_sky_masking`, `evaluated_resolution`, `benchmark_depth_range_m` | `fid_backend`, `spatial_alignment` |
|
|
250
|
+
| `rgb` | `depth`, `calibration`, `segmentation` | `samples`, `gt_sky_masking`, `missing_segmentation`, `evaluated_resolution`, `benchmark_depth_range_m` | `fid_backend`, `spatial_alignment` |
|
|
237
251
|
| `rays` | `calibration` | `samples`, `evaluated_resolution` | `spatial_alignment` |
|
|
238
|
-
| `points3d` (dense) | `intrinsics`, `calibration`, `segmentation` | `samples`, `gt_sky_masking`, `evaluated_resolution` | `points_3d_alignment`, `spatial_alignment` |
|
|
239
|
-
| `points3d` (sparse GT) | `intrinsics`, `camera_extrinsics`, `lidar_extrinsics`, `segmentation` | `samples`, `gt_sky_masking`, `evaluated_resolution` | `points_3d_alignment` |
|
|
252
|
+
| `points3d` (dense) | `intrinsics`, `calibration`, `segmentation` | `samples`, `gt_sky_masking`, `missing_segmentation`, `evaluated_resolution` | `points_3d_alignment`, `spatial_alignment` |
|
|
253
|
+
| `points3d` (sparse GT) | `intrinsics`, `camera_extrinsics`, `lidar_extrinsics`, `segmentation` | `samples`, `gt_sky_masking`, `missing_segmentation`, `evaluated_resolution` | `points_3d_alignment` |
|
|
240
254
|
|
|
241
255
|
Every modality also has `method.metrics_revision`.
|
|
242
256
|
|
|
@@ -29,6 +29,7 @@ from .config_paths import (
|
|
|
29
29
|
resolve_gt_segmentation_path,
|
|
30
30
|
)
|
|
31
31
|
from .data import (
|
|
32
|
+
PartialInputDataset,
|
|
32
33
|
build_depth_eval_dataset,
|
|
33
34
|
build_points_3d_eval_dataset,
|
|
34
35
|
build_points_3d_sparse_eval_dataset,
|
|
@@ -1478,7 +1479,12 @@ _PAIRED_OPTIONAL_INPUTS = {
|
|
|
1478
1479
|
|
|
1479
1480
|
|
|
1480
1481
|
def _build_eval_dataset(
|
|
1481
|
-
build,
|
|
1482
|
+
build,
|
|
1483
|
+
kwargs: dict,
|
|
1484
|
+
*,
|
|
1485
|
+
ds_name: str,
|
|
1486
|
+
iou_only: tuple[str, ...] = (),
|
|
1487
|
+
missing_segmentation: str = "drop",
|
|
1482
1488
|
) -> tuple:
|
|
1483
1489
|
"""Build an eval dataset and account for the frames its optional inputs drop.
|
|
1484
1490
|
|
|
@@ -1488,13 +1494,24 @@ def _build_eval_dataset(
|
|
|
1488
1494
|
only for ``--sky-iou``, which must not change the evaluated pairs: if it
|
|
1489
1495
|
lacks any, the dataset is rebuilt without it.
|
|
1490
1496
|
|
|
1497
|
+
With *missing_segmentation* ``"unmasked"``, frames without their own GT
|
|
1498
|
+
segmentation are kept instead: they are read without it, so ``--mask-sky``
|
|
1499
|
+
leaves them unmasked and ``--sky-iou`` leaves them out of the IoU.
|
|
1500
|
+
|
|
1491
1501
|
Returns ``(dataset, skipped)`` with ``{input: reason}`` for every
|
|
1492
1502
|
*iou_only* input that was left out.
|
|
1493
1503
|
"""
|
|
1494
1504
|
optional = tuple(_PAIRED_OPTIONAL_INPUTS)
|
|
1495
1505
|
dataset = build(**kwargs)
|
|
1506
|
+
partial_segmentation = (
|
|
1507
|
+
missing_segmentation == "unmasked"
|
|
1508
|
+
and kwargs.get("segmentation_path") is not None
|
|
1509
|
+
and bool(count_ids_dropped_by(dataset, "segmentation", optional=optional))
|
|
1510
|
+
)
|
|
1496
1511
|
skipped = {}
|
|
1497
1512
|
for name in iou_only:
|
|
1513
|
+
if name == "segmentation" and partial_segmentation:
|
|
1514
|
+
continue
|
|
1498
1515
|
dropped = count_ids_dropped_by(dataset, name, optional=optional)
|
|
1499
1516
|
if not dropped:
|
|
1500
1517
|
continue
|
|
@@ -1510,11 +1527,23 @@ def _build_eval_dataset(
|
|
|
1510
1527
|
dataset = build(**kwargs)
|
|
1511
1528
|
for name, (_, label) in _PAIRED_OPTIONAL_INPUTS.items():
|
|
1512
1529
|
dropped = count_ids_dropped_by(dataset, name, optional=optional)
|
|
1513
|
-
if dropped:
|
|
1530
|
+
if not dropped:
|
|
1531
|
+
continue
|
|
1532
|
+
if name == "segmentation" and partial_segmentation:
|
|
1533
|
+
print(
|
|
1534
|
+
f"Note: the {label} of '{ds_name}' lacks {dropped} pairs that the "
|
|
1535
|
+
"other inputs share; with --missing-segmentation unmasked they "
|
|
1536
|
+
"are evaluated without GT sky masking and left out of the sky IoU."
|
|
1537
|
+
)
|
|
1538
|
+
else:
|
|
1514
1539
|
print(
|
|
1515
1540
|
f"Warning: the {label} of '{ds_name}' lacks {dropped} pairs that "
|
|
1516
1541
|
"the other inputs share; those pairs are not evaluated."
|
|
1517
1542
|
)
|
|
1543
|
+
if partial_segmentation:
|
|
1544
|
+
keys, _ = _PAIRED_OPTIONAL_INPUTS["segmentation"]
|
|
1545
|
+
complete = build(**{**kwargs, **dict.fromkeys(keys)})
|
|
1546
|
+
dataset = PartialInputDataset(complete, dataset)
|
|
1518
1547
|
return dataset, skipped
|
|
1519
1548
|
|
|
1520
1549
|
|
|
@@ -1828,6 +1857,16 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
1828
1857
|
action="store_true",
|
|
1829
1858
|
help="Mask sky regions from metrics using GT segmentation",
|
|
1830
1859
|
)
|
|
1860
|
+
parser.add_argument(
|
|
1861
|
+
"--missing-segmentation",
|
|
1862
|
+
choices=["drop", "unmasked"],
|
|
1863
|
+
default="drop",
|
|
1864
|
+
help=(
|
|
1865
|
+
"Frames without their own GT segmentation: 'drop' leaves them out "
|
|
1866
|
+
"(default); 'unmasked' evaluates them without GT sky masking and "
|
|
1867
|
+
"leaves them out of the sky IoU"
|
|
1868
|
+
),
|
|
1869
|
+
)
|
|
1831
1870
|
parser.add_argument(
|
|
1832
1871
|
"--mask-sky-prediction",
|
|
1833
1872
|
action="store_true",
|
|
@@ -2172,6 +2211,7 @@ def _evaluate(
|
|
|
2172
2211
|
),
|
|
2173
2212
|
ds_name=ds_name,
|
|
2174
2213
|
iou_only=iou_only_inputs,
|
|
2214
|
+
missing_segmentation=args.missing_segmentation,
|
|
2175
2215
|
)
|
|
2176
2216
|
et_eval_datasets["depth"] = depth_dataset
|
|
2177
2217
|
|
|
@@ -2259,6 +2299,9 @@ def _evaluate(
|
|
|
2259
2299
|
population={
|
|
2260
2300
|
"samples": depth_dataset_info.get("num_pairs"),
|
|
2261
2301
|
"gt_sky_masking": bool(args.mask_sky),
|
|
2302
|
+
"missing_segmentation": (
|
|
2303
|
+
args.missing_segmentation if args.mask_sky else None
|
|
2304
|
+
),
|
|
2262
2305
|
"pred_sky_masking": bool(args.mask_sky_prediction),
|
|
2263
2306
|
"evaluated_resolution": evaluated_resolution(depth_spatial),
|
|
2264
2307
|
"benchmark_depth_range_m": protocol_benchmark_range,
|
|
@@ -2302,6 +2345,7 @@ def _evaluate(
|
|
|
2302
2345
|
),
|
|
2303
2346
|
},
|
|
2304
2347
|
"modality_params": depth_meta,
|
|
2348
|
+
"gt_sky_masking": depth_results.get("gt_sky_masking"),
|
|
2305
2349
|
"sky_segmentation": _sky_segmentation_meta(
|
|
2306
2350
|
depth_results, segmentation_key, skipped_inputs
|
|
2307
2351
|
),
|
|
@@ -2438,6 +2482,7 @@ def _evaluate(
|
|
|
2438
2482
|
),
|
|
2439
2483
|
ds_name=ds_name,
|
|
2440
2484
|
iou_only=iou_only_inputs,
|
|
2485
|
+
missing_segmentation=args.missing_segmentation,
|
|
2441
2486
|
)
|
|
2442
2487
|
et_eval_datasets["sparse_depth"] = sparse_depth_dataset
|
|
2443
2488
|
|
|
@@ -2523,6 +2568,9 @@ def _evaluate(
|
|
|
2523
2568
|
population={
|
|
2524
2569
|
"samples": sparse_depth_dataset_info.get("num_pairs"),
|
|
2525
2570
|
"gt_sky_masking": bool(args.mask_sky),
|
|
2571
|
+
"missing_segmentation": (
|
|
2572
|
+
args.missing_segmentation if args.mask_sky else None
|
|
2573
|
+
),
|
|
2526
2574
|
"pred_sky_masking": bool(args.mask_sky_prediction),
|
|
2527
2575
|
"evaluated_resolution": evaluated_resolution(
|
|
2528
2576
|
sparse_depth_spatial
|
|
@@ -2577,6 +2625,7 @@ def _evaluate(
|
|
|
2577
2625
|
),
|
|
2578
2626
|
},
|
|
2579
2627
|
"modality_params": sparse_depth_meta,
|
|
2628
|
+
"gt_sky_masking": sparse_depth_results.get("gt_sky_masking"),
|
|
2580
2629
|
"sky_segmentation": _sky_segmentation_meta(
|
|
2581
2630
|
sparse_depth_results, segmentation_key, skipped_inputs
|
|
2582
2631
|
),
|
|
@@ -2707,6 +2756,7 @@ def _evaluate(
|
|
|
2707
2756
|
segmentation_modality_key=segmentation_key or "segmentation",
|
|
2708
2757
|
),
|
|
2709
2758
|
ds_name=ds_name,
|
|
2759
|
+
missing_segmentation=args.missing_segmentation,
|
|
2710
2760
|
)
|
|
2711
2761
|
et_eval_datasets["rgb"] = rgb_dataset
|
|
2712
2762
|
|
|
@@ -2776,6 +2826,9 @@ def _evaluate(
|
|
|
2776
2826
|
population={
|
|
2777
2827
|
"samples": rgb_dataset_info.get("num_pairs"),
|
|
2778
2828
|
"gt_sky_masking": bool(args.mask_sky),
|
|
2829
|
+
"missing_segmentation": (
|
|
2830
|
+
args.missing_segmentation if args.mask_sky else None
|
|
2831
|
+
),
|
|
2779
2832
|
"evaluated_resolution": evaluated_resolution(rgb_spatial),
|
|
2780
2833
|
"benchmark_depth_range_m": protocol_benchmark_range,
|
|
2781
2834
|
},
|
|
@@ -2807,6 +2860,7 @@ def _evaluate(
|
|
|
2807
2860
|
),
|
|
2808
2861
|
},
|
|
2809
2862
|
"modality_params": rgb_meta,
|
|
2863
|
+
"gt_sky_masking": rgb_results.get("gt_sky_masking"),
|
|
2810
2864
|
"eval_params": {
|
|
2811
2865
|
"sky_masking": args.mask_sky,
|
|
2812
2866
|
"fid_backend": args.rgb_fid_backend,
|
|
@@ -3028,6 +3082,7 @@ def _evaluate(
|
|
|
3028
3082
|
segmentation_modality_key=segmentation_key or "segmentation",
|
|
3029
3083
|
),
|
|
3030
3084
|
ds_name=ds_name,
|
|
3085
|
+
missing_segmentation=args.missing_segmentation,
|
|
3031
3086
|
)
|
|
3032
3087
|
et_eval_datasets["points_3d"] = points_3d_dataset
|
|
3033
3088
|
|
|
@@ -3099,6 +3154,9 @@ def _evaluate(
|
|
|
3099
3154
|
population={
|
|
3100
3155
|
"samples": p3_dataset_info.get("num_pairs"),
|
|
3101
3156
|
"gt_sky_masking": bool(args.mask_sky),
|
|
3157
|
+
"missing_segmentation": (
|
|
3158
|
+
args.missing_segmentation if args.mask_sky else None
|
|
3159
|
+
),
|
|
3102
3160
|
"evaluated_resolution": evaluated_resolution(p3_spatial),
|
|
3103
3161
|
},
|
|
3104
3162
|
method={
|
|
@@ -3130,6 +3188,7 @@ def _evaluate(
|
|
|
3130
3188
|
),
|
|
3131
3189
|
},
|
|
3132
3190
|
"modality_params": points_3d_meta,
|
|
3191
|
+
"gt_sky_masking": points_3d_results.get("gt_sky_masking"),
|
|
3133
3192
|
"eval_params": {
|
|
3134
3193
|
"sky_masking": args.mask_sky,
|
|
3135
3194
|
"points_3d_alignment_mode": args.points_3d_alignment,
|
|
@@ -3223,6 +3282,7 @@ def _evaluate(
|
|
|
3223
3282
|
segmentation_split=segmentation_split,
|
|
3224
3283
|
),
|
|
3225
3284
|
ds_name=ds_name,
|
|
3285
|
+
missing_segmentation=args.missing_segmentation,
|
|
3226
3286
|
)
|
|
3227
3287
|
p3ds_meta = get_points_3d_metadata(points_3d_sparse_dataset)
|
|
3228
3288
|
p3s_alignment_mode = args.points_3d_alignment
|
|
@@ -3298,6 +3358,9 @@ def _evaluate(
|
|
|
3298
3358
|
population={
|
|
3299
3359
|
"samples": p3s_dataset_info.get("num_pairs"),
|
|
3300
3360
|
"gt_sky_masking": bool(args.mask_sky),
|
|
3361
|
+
"missing_segmentation": (
|
|
3362
|
+
args.missing_segmentation if args.mask_sky else None
|
|
3363
|
+
),
|
|
3301
3364
|
"evaluated_resolution": evaluated_resolution(p3s_spatial),
|
|
3302
3365
|
},
|
|
3303
3366
|
method={
|
|
@@ -3337,6 +3400,7 @@ def _evaluate(
|
|
|
3337
3400
|
),
|
|
3338
3401
|
},
|
|
3339
3402
|
"modality_params": p3ds_meta,
|
|
3403
|
+
"gt_sky_masking": points_3d_sparse_results.get("gt_sky_masking"),
|
|
3340
3404
|
"eval_params": {
|
|
3341
3405
|
"sky_masking": args.mask_sky,
|
|
3342
3406
|
"alignment_mode": p3s_alignment_mode,
|
|
@@ -1117,6 +1117,60 @@ def build_rgb_eval_dataset(
|
|
|
1117
1117
|
)
|
|
1118
1118
|
|
|
1119
1119
|
|
|
1120
|
+
class PartialInputDataset:
|
|
1121
|
+
"""Every frame of *complete*, with an optional input where a frame has it.
|
|
1122
|
+
|
|
1123
|
+
euler-loading intersects every modality by ID, so an input such as GT
|
|
1124
|
+
segmentation that covers only some frames removes the others from the
|
|
1125
|
+
pairing. This dataset keeps them: *complete* is built without the input
|
|
1126
|
+
and *with_input* with it, from the same other inputs, so its frames are a
|
|
1127
|
+
subset of *complete*'s. A frame of *with_input* is read from it, any
|
|
1128
|
+
other frame from *complete*, whose sample then has no entry for the
|
|
1129
|
+
input. Frames keep *complete*'s order and identities.
|
|
1130
|
+
|
|
1131
|
+
Public attributes such as ``get_modality_metadata`` and ``modality_paths``
|
|
1132
|
+
come from *with_input*, which describes every input.
|
|
1133
|
+
"""
|
|
1134
|
+
|
|
1135
|
+
def __init__(self, complete: Any, with_input: Any) -> None:
|
|
1136
|
+
self._complete = complete
|
|
1137
|
+
self._with_input = with_input
|
|
1138
|
+
index = {
|
|
1139
|
+
with_input.sample_full_id(j): j for j in range(len(with_input))
|
|
1140
|
+
}
|
|
1141
|
+
self._source = [
|
|
1142
|
+
index.pop(complete.sample_full_id(i), None) for i in range(len(complete))
|
|
1143
|
+
]
|
|
1144
|
+
if index:
|
|
1145
|
+
raise ValueError(
|
|
1146
|
+
f"{len(index)} frames with the optional input are not among the "
|
|
1147
|
+
"frames without it; both datasets must share their other inputs"
|
|
1148
|
+
)
|
|
1149
|
+
|
|
1150
|
+
@property
|
|
1151
|
+
def frames_without_input(self) -> int:
|
|
1152
|
+
return sum(source is None for source in self._source)
|
|
1153
|
+
|
|
1154
|
+
def __len__(self) -> int:
|
|
1155
|
+
return len(self._source)
|
|
1156
|
+
|
|
1157
|
+
def __getitem__(self, index: int) -> dict[str, Any]:
|
|
1158
|
+
source = self._source[index]
|
|
1159
|
+
if source is None:
|
|
1160
|
+
return self._complete[index]
|
|
1161
|
+
return self._with_input[source]
|
|
1162
|
+
|
|
1163
|
+
def sample_full_id(self, index: int, modality_name: Optional[str] = None) -> str:
|
|
1164
|
+
return self._complete.sample_full_id(index, modality_name)
|
|
1165
|
+
|
|
1166
|
+
def __getattr__(self, name: str) -> Any:
|
|
1167
|
+
# Private and dunder lookups (pickling, ``_lookups``) are not forwarded:
|
|
1168
|
+
# they would describe *with_input*'s frames, not this dataset's.
|
|
1169
|
+
if name.startswith("_"):
|
|
1170
|
+
raise AttributeError(name)
|
|
1171
|
+
return getattr(self._with_input, name)
|
|
1172
|
+
|
|
1173
|
+
|
|
1120
1174
|
def count_ids_dropped_by(
|
|
1121
1175
|
dataset: Any, modality: str, *, optional: tuple[str, ...] = ()
|
|
1122
1176
|
) -> Optional[int]:
|
|
@@ -588,6 +588,14 @@ class _SkyMaskCoverage:
|
|
|
588
588
|
elif not np.all(sky_valid):
|
|
589
589
|
self.with_sky += 1
|
|
590
590
|
|
|
591
|
+
def info(self) -> dict:
|
|
592
|
+
"""Return provenance for ``meta.gt_sky_masking`` in ``eval.json``."""
|
|
593
|
+
return {
|
|
594
|
+
"samples": self.samples,
|
|
595
|
+
"samples_without_segmentation": self.missing,
|
|
596
|
+
"samples_with_sky": self.with_sky,
|
|
597
|
+
}
|
|
598
|
+
|
|
591
599
|
def warn_if_empty(self, modality: str) -> None:
|
|
592
600
|
if self.missing:
|
|
593
601
|
print(
|
|
@@ -2042,6 +2050,8 @@ def evaluate_depth_samples(
|
|
|
2042
2050
|
result["sky_segmentation_info"] = sky_iou_scores.info()
|
|
2043
2051
|
if pred_sky_masking is not None:
|
|
2044
2052
|
result["pred_sky_masking"] = pred_sky_masking.info()
|
|
2053
|
+
if sky_mask_enabled:
|
|
2054
|
+
result["gt_sky_masking"] = sky_coverage.info()
|
|
2045
2055
|
return _finalize_depth_distributions(
|
|
2046
2056
|
result, distribution_config, "depth", benchmark_depth_range, sky_depth
|
|
2047
2057
|
)
|
|
@@ -2676,6 +2686,8 @@ def evaluate_sparse_depth_samples(
|
|
|
2676
2686
|
result["sky_segmentation_info"] = sky_iou_scores.info()
|
|
2677
2687
|
if pred_sky_masking is not None:
|
|
2678
2688
|
result["pred_sky_masking"] = pred_sky_masking.info()
|
|
2689
|
+
if sky_mask_enabled:
|
|
2690
|
+
result["gt_sky_masking"] = sky_coverage.info()
|
|
2679
2691
|
return _finalize_depth_distributions(
|
|
2680
2692
|
result, distribution_config, "sparse_depth",
|
|
2681
2693
|
benchmark_depth_range, sky_depth,
|
|
@@ -3246,6 +3258,7 @@ def evaluate_rgb_samples(
|
|
|
3246
3258
|
return {
|
|
3247
3259
|
"rgb": rgb_results,
|
|
3248
3260
|
"rgb_benchmark": rgb_benchmark,
|
|
3261
|
+
"gt_sky_masking": sky_coverage.info() if sky_mask_enabled else None,
|
|
3249
3262
|
"per_file_metrics": per_file_metrics,
|
|
3250
3263
|
"dataset_info": {
|
|
3251
3264
|
"num_pairs": num_samples,
|
|
@@ -4083,6 +4096,8 @@ def evaluate_points_3d_samples(
|
|
|
4083
4096
|
else None,
|
|
4084
4097
|
},
|
|
4085
4098
|
}
|
|
4099
|
+
if sky_mask_enabled:
|
|
4100
|
+
result["gt_sky_masking"] = sky_coverage.info()
|
|
4086
4101
|
return finalize_distributions(result, distribution_config, "rmse3d", "points_3d")
|
|
4087
4102
|
finally:
|
|
4088
4103
|
for st in stores.values():
|
|
@@ -4819,6 +4834,8 @@ def evaluate_points_3d_sparse_samples(
|
|
|
4819
4834
|
else None,
|
|
4820
4835
|
},
|
|
4821
4836
|
}
|
|
4837
|
+
if sky_mask_enabled:
|
|
4838
|
+
result["gt_sky_masking"] = sky_coverage.info()
|
|
4822
4839
|
return finalize_distributions(result, distribution_config, "rmse3d", "points_3d")
|
|
4823
4840
|
finally:
|
|
4824
4841
|
for st in stores.values():
|
|
@@ -30,7 +30,7 @@ from .config_paths import (
|
|
|
30
30
|
|
|
31
31
|
#: Version of the ``protocol`` block. Renaming or removing a key is a major
|
|
32
32
|
#: bump; adding one is a minor bump.
|
|
33
|
-
SCHEMA_VERSION = "1.
|
|
33
|
+
SCHEMA_VERSION = "1.2"
|
|
34
34
|
|
|
35
35
|
#: Bump whenever a release changes metric values for identical inputs and
|
|
36
36
|
#: settings, and say so in the CHANGELOG. Evaluations with different revisions
|
|
@@ -195,3 +195,130 @@ def test_every_builder_pairs_segmentation_by_id(monkeypatch, build, kwargs):
|
|
|
195
195
|
dataset = build(**kwargs, segmentation_path="/seg")
|
|
196
196
|
assert dataset.modalities["segmentation"].loader == "sky"
|
|
197
197
|
assert "segmentation" not in dataset.hierarchical_modalities
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
# ---------------------------------------------------------------------------
|
|
201
|
+
# --missing-segmentation unmasked
|
|
202
|
+
# ---------------------------------------------------------------------------
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def test_missing_segmentation_unmasked_evaluates_those_frames_unmasked(
|
|
206
|
+
monkeypatch, capsys, flat_frames
|
|
207
|
+
):
|
|
208
|
+
_patch_depth_metrics(monkeypatch)
|
|
209
|
+
config_path, paths = flat_frames(segmentation_frames=("f0", "f1"))
|
|
210
|
+
|
|
211
|
+
saved = _run(
|
|
212
|
+
monkeypatch, config_path, paths["pred"],
|
|
213
|
+
"--mask-sky", "--missing-segmentation", "unmasked",
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
assert saved["dataset_info"]["num_pairs"] == 3
|
|
217
|
+
# f0 and f1 are masked with their own sky; f2 keeps its sky row: 900 m
|
|
218
|
+
# error on 4 of 16 pixels.
|
|
219
|
+
assert _per_file_mae(saved) == {"f0": 0.0, "f1": 0.0, "f2": 225.0}
|
|
220
|
+
assert saved["meta"]["gt_sky_masking"] == {
|
|
221
|
+
"samples": 3, "samples_without_segmentation": 1, "samples_with_sky": 2,
|
|
222
|
+
}
|
|
223
|
+
parameters = saved["protocol"]["parameters"]
|
|
224
|
+
assert parameters["population.gt_sky_masking"] is True
|
|
225
|
+
assert parameters["population.missing_segmentation"] == "unmasked"
|
|
226
|
+
out = capsys.readouterr().out
|
|
227
|
+
assert (
|
|
228
|
+
"the GT segmentation of 'model' lacks 1 pairs that the other inputs "
|
|
229
|
+
"share; with --missing-segmentation unmasked they are evaluated without "
|
|
230
|
+
"GT sky masking"
|
|
231
|
+
) in out
|
|
232
|
+
assert "found no GT segmentation of their own frame for 1 of the 3" in out
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def test_missing_segmentation_unmasked_matches_drop_on_covered_frames(
|
|
236
|
+
monkeypatch, flat_frames
|
|
237
|
+
):
|
|
238
|
+
_patch_depth_metrics(monkeypatch)
|
|
239
|
+
config_path, paths = flat_frames(segmentation_frames=("f0", "f1"))
|
|
240
|
+
|
|
241
|
+
dropped = _run(monkeypatch, config_path, paths["pred"], "--mask-sky")
|
|
242
|
+
kept = _run(
|
|
243
|
+
monkeypatch, config_path, paths["pred"],
|
|
244
|
+
"--mask-sky", "--missing-segmentation", "unmasked",
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
covered = {k: v for k, v in _per_file_mae(kept).items() if k != "f2"}
|
|
248
|
+
assert covered == _per_file_mae(dropped)
|
|
249
|
+
assert dropped["protocol"]["parameters"]["population.missing_segmentation"] == "drop"
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def test_missing_segmentation_unmasked_scores_sky_iou_on_covered_frames(
|
|
253
|
+
monkeypatch, flat_frames
|
|
254
|
+
):
|
|
255
|
+
_patch_depth_metrics(monkeypatch)
|
|
256
|
+
config_path, paths = flat_frames(segmentation_frames=("f0", "f1"))
|
|
257
|
+
|
|
258
|
+
saved = _run(
|
|
259
|
+
monkeypatch, config_path, paths["pred"],
|
|
260
|
+
"--sky-iou", "--sky-depth", "50", "--missing-segmentation", "unmasked",
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
assert saved["dataset_info"]["num_pairs"] == 3
|
|
264
|
+
meta = saved["meta"]["sky_segmentation"]
|
|
265
|
+
assert meta["implicit"]["available"] is True
|
|
266
|
+
assert meta["samples"] == 3
|
|
267
|
+
assert meta["samples_without_gt"] == 1
|
|
268
|
+
# Without --mask-sky, the policy is not a comparison parameter.
|
|
269
|
+
assert saved["protocol"]["parameters"]["population.missing_segmentation"] is None
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def test_missing_segmentation_unmasked_with_prefetch_workers(monkeypatch, flat_frames):
|
|
273
|
+
"""Worker processes receive the combined dataset intact."""
|
|
274
|
+
_patch_depth_metrics(monkeypatch)
|
|
275
|
+
config_path, paths = flat_frames(segmentation_frames=("f0", "f1"))
|
|
276
|
+
|
|
277
|
+
saved = _run(
|
|
278
|
+
monkeypatch, config_path, paths["pred"],
|
|
279
|
+
"--mask-sky", "--missing-segmentation", "unmasked", "--num-workers", "2",
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
assert _per_file_mae(saved) == {"f0": 0.0, "f1": 0.0, "f2": 225.0}
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
class _Frames:
|
|
286
|
+
def __init__(self, ids, tag):
|
|
287
|
+
self.ids, self.tag = list(ids), tag
|
|
288
|
+
self.public = f"{tag} attribute"
|
|
289
|
+
|
|
290
|
+
def __len__(self):
|
|
291
|
+
return len(self.ids)
|
|
292
|
+
|
|
293
|
+
def __getitem__(self, index):
|
|
294
|
+
return {"id": self.ids[index], "source": self.tag}
|
|
295
|
+
|
|
296
|
+
def sample_full_id(self, index, modality_name=None):
|
|
297
|
+
return f"/{self.ids[index]}"
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def test_partial_input_dataset_keeps_every_frame_in_order():
|
|
301
|
+
import pickle
|
|
302
|
+
|
|
303
|
+
dataset = data.PartialInputDataset(
|
|
304
|
+
_Frames(["a", "b", "c"], "complete"), _Frames(["a", "c"], "with")
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
assert len(dataset) == 3
|
|
308
|
+
assert [dataset[i] for i in range(3)] == [
|
|
309
|
+
{"id": "a", "source": "with"},
|
|
310
|
+
{"id": "b", "source": "complete"},
|
|
311
|
+
{"id": "c", "source": "with"},
|
|
312
|
+
]
|
|
313
|
+
assert dataset.frames_without_input == 1
|
|
314
|
+
assert dataset.sample_full_id(1) == "/b"
|
|
315
|
+
assert dataset.public == "with attribute"
|
|
316
|
+
with pytest.raises(AttributeError):
|
|
317
|
+
dataset._lookups
|
|
318
|
+
restored = pickle.loads(pickle.dumps(dataset))
|
|
319
|
+
assert [restored[i]["source"] for i in range(3)] == ["with", "complete", "with"]
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def test_partial_input_dataset_rejects_frames_missing_from_the_complete_set():
|
|
323
|
+
with pytest.raises(ValueError, match="1 frames with the optional input"):
|
|
324
|
+
data.PartialInputDataset(_Frames(["a"], "complete"), _Frames(["a", "z"], "with"))
|
|
@@ -37,6 +37,7 @@ EXPECTED_KEYS = {
|
|
|
37
37
|
"depth": _reference_keys("calibration", "segmentation") | {
|
|
38
38
|
"population.samples",
|
|
39
39
|
"population.gt_sky_masking",
|
|
40
|
+
"population.missing_segmentation",
|
|
40
41
|
"population.pred_sky_masking",
|
|
41
42
|
"population.evaluated_resolution",
|
|
42
43
|
"population.benchmark_depth_range_m",
|
|
@@ -49,6 +50,7 @@ EXPECTED_KEYS = {
|
|
|
49
50
|
"sparsedepth": _reference_keys(*SPARSE_INPUTS) | {
|
|
50
51
|
"population.samples",
|
|
51
52
|
"population.gt_sky_masking",
|
|
53
|
+
"population.missing_segmentation",
|
|
52
54
|
"population.pred_sky_masking",
|
|
53
55
|
"population.evaluated_resolution",
|
|
54
56
|
"population.benchmark_depth_range_m",
|
|
@@ -60,6 +62,7 @@ EXPECTED_KEYS = {
|
|
|
60
62
|
"rgb": _reference_keys("depth", "calibration", "segmentation") | {
|
|
61
63
|
"population.samples",
|
|
62
64
|
"population.gt_sky_masking",
|
|
65
|
+
"population.missing_segmentation",
|
|
63
66
|
"population.evaluated_resolution",
|
|
64
67
|
"population.benchmark_depth_range_m",
|
|
65
68
|
"method.metrics_revision",
|
|
@@ -75,6 +78,7 @@ EXPECTED_KEYS = {
|
|
|
75
78
|
"points3d": _reference_keys("intrinsics", "calibration", "segmentation") | {
|
|
76
79
|
"population.samples",
|
|
77
80
|
"population.gt_sky_masking",
|
|
81
|
+
"population.missing_segmentation",
|
|
78
82
|
"population.evaluated_resolution",
|
|
79
83
|
"method.metrics_revision",
|
|
80
84
|
"method.points_3d_alignment",
|
|
@@ -83,6 +87,7 @@ EXPECTED_KEYS = {
|
|
|
83
87
|
"points3d_sparse": _reference_keys(*SPARSE_INPUTS) | {
|
|
84
88
|
"population.samples",
|
|
85
89
|
"population.gt_sky_masking",
|
|
90
|
+
"population.missing_segmentation",
|
|
86
91
|
"population.evaluated_resolution",
|
|
87
92
|
"method.metrics_revision",
|
|
88
93
|
"method.points_3d_alignment",
|
|
@@ -250,6 +255,7 @@ def test_depth_parameters(monkeypatch, tmp_path, depth_paths):
|
|
|
250
255
|
assert parameters["reference.calibration.dataset"] is None
|
|
251
256
|
assert parameters["population.samples"] == 2
|
|
252
257
|
assert parameters["population.gt_sky_masking"] is False
|
|
258
|
+
assert parameters["population.missing_segmentation"] is None
|
|
253
259
|
assert parameters["population.evaluated_resolution"] == [2, 2]
|
|
254
260
|
assert parameters["population.benchmark_depth_range_m"] is None
|
|
255
261
|
assert parameters["method.metrics_revision"] == METRICS_REVISION
|
|
@@ -267,7 +273,12 @@ def test_depth_parameters(monkeypatch, tmp_path, depth_paths):
|
|
|
267
273
|
(("--depth-alignment", "affine"), {"method.depth_alignment"}),
|
|
268
274
|
# The config has a sky_mask, which both settings then score with.
|
|
269
275
|
(("--sky-depth", "50"), {"method.sky_depth_m", "method.pred_sky_mask"}),
|
|
270
|
-
(
|
|
276
|
+
(
|
|
277
|
+
("--mask-sky",),
|
|
278
|
+
{"population.gt_sky_masking", "population.missing_segmentation"},
|
|
279
|
+
),
|
|
280
|
+
# Without --mask-sky, what happens to unsegmented frames changes nothing.
|
|
281
|
+
(("--missing-segmentation", "unmasked"), set()),
|
|
271
282
|
(
|
|
272
283
|
("--mask-sky-prediction",),
|
|
273
284
|
{"population.pred_sky_masking", "method.pred_sky_mask"},
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{euler_eval-2.35.0 → euler_eval-2.36.0}/docs/feature-request-unified-evaluation-framework.md
RENAMED
|
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
|
|
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
|
{euler_eval-2.35.0 → euler_eval-2.36.0}/tests/fixtures/distributions/counts-points3d.eval.json
RENAMED
|
File without changes
|
{euler_eval-2.35.0 → euler_eval-2.36.0}/tests/fixtures/distributions/counts-sparsedepth.eval.json
RENAMED
|
File without changes
|
{euler_eval-2.35.0 → euler_eval-2.36.0}/tests/fixtures/distributions/future-sum-by-depth.eval.json
RENAMED
|
File without changes
|
{euler_eval-2.35.0 → euler_eval-2.36.0}/tests/fixtures/distributions/histogram-only.eval.json
RENAMED
|
File without changes
|
{euler_eval-2.35.0 → euler_eval-2.36.0}/tests/fixtures/distributions/per-file-only.eval.json
RENAMED
|
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
|