euler-eval 2.9.0__tar.gz → 2.10.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.
Files changed (55) hide show
  1. {euler_eval-2.9.0 → euler_eval-2.10.0}/PKG-INFO +5 -3
  2. {euler_eval-2.9.0 → euler_eval-2.10.0}/README.md +4 -2
  3. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/cli.py +43 -4
  4. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/data.py +6 -5
  5. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval.egg-info/PKG-INFO +5 -3
  6. {euler_eval-2.9.0 → euler_eval-2.10.0}/pyproject.toml +1 -1
  7. {euler_eval-2.9.0 → euler_eval-2.10.0}/tests/test_config.py +25 -0
  8. {euler_eval-2.9.0 → euler_eval-2.10.0}/tests/test_data_builders.py +23 -0
  9. {euler_eval-2.9.0 → euler_eval-2.10.0}/tests/test_save_results.py +15 -0
  10. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/__init__.py +0 -0
  11. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/evaluate.py +0 -0
  12. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/metrics/__init__.py +0 -0
  13. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/metrics/absrel.py +0 -0
  14. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/metrics/daniel_error.py +0 -0
  15. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/metrics/depth_binned_error.py +0 -0
  16. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/metrics/depth_edge_f1.py +0 -0
  17. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/metrics/depth_standard.py +0 -0
  18. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/metrics/fid_kid.py +0 -0
  19. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/metrics/gpu_depth_batch.py +0 -0
  20. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/metrics/gpu_image_batch.py +0 -0
  21. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/metrics/high_freq_energy.py +0 -0
  22. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/metrics/lpips_metric.py +0 -0
  23. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/metrics/normal_consistency.py +0 -0
  24. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/metrics/psnr.py +0 -0
  25. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/metrics/rgb_edge_f1.py +0 -0
  26. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/metrics/rgb_lpips.py +0 -0
  27. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/metrics/rgb_psnr_ssim.py +0 -0
  28. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/metrics/rho_a.py +0 -0
  29. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/metrics/rmse.py +0 -0
  30. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/metrics/scale_invariant_log.py +0 -0
  31. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/metrics/ssim.py +0 -0
  32. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/metrics/tail_errors.py +0 -0
  33. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/metrics/utils.py +0 -0
  34. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/sanity_checker.py +0 -0
  35. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval/utils/hierarchy_parser.py +0 -0
  36. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval.egg-info/SOURCES.txt +0 -0
  37. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval.egg-info/dependency_links.txt +0 -0
  38. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval.egg-info/entry_points.txt +0 -0
  39. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval.egg-info/requires.txt +0 -0
  40. {euler_eval-2.9.0 → euler_eval-2.10.0}/euler_eval.egg-info/top_level.txt +0 -0
  41. {euler_eval-2.9.0 → euler_eval-2.10.0}/init_cache.py +0 -0
  42. {euler_eval-2.9.0 → euler_eval-2.10.0}/setup.cfg +0 -0
  43. {euler_eval-2.9.0 → euler_eval-2.10.0}/tests/test_alignment.py +0 -0
  44. {euler_eval-2.9.0 → euler_eval-2.10.0}/tests/test_cli_device.py +0 -0
  45. {euler_eval-2.9.0 → euler_eval-2.10.0}/tests/test_data.py +0 -0
  46. {euler_eval-2.9.0 → euler_eval-2.10.0}/tests/test_depth_alignment_output.py +0 -0
  47. {euler_eval-2.9.0 → euler_eval-2.10.0}/tests/test_depth_standard.py +0 -0
  48. {euler_eval-2.9.0 → euler_eval-2.10.0}/tests/test_evaluate_helpers.py +0 -0
  49. {euler_eval-2.9.0 → euler_eval-2.10.0}/tests/test_init_cache.py +0 -0
  50. {euler_eval-2.9.0 → euler_eval-2.10.0}/tests/test_integration.py +0 -0
  51. {euler_eval-2.9.0 → euler_eval-2.10.0}/tests/test_meta_output.py +0 -0
  52. {euler_eval-2.9.0 → euler_eval-2.10.0}/tests/test_metrics_utils.py +0 -0
  53. {euler_eval-2.9.0 → euler_eval-2.10.0}/tests/test_rgb_fid_output.py +0 -0
  54. {euler_eval-2.9.0 → euler_eval-2.10.0}/tests/test_rho_a.py +0 -0
  55. {euler_eval-2.9.0 → euler_eval-2.10.0}/tests/test_sparse_depth.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: euler-eval
3
- Version: 2.9.0
3
+ Version: 2.10.0
4
4
  Summary: Depth map evaluation toolkit with comprehensive metrics
5
5
  Author: Depth Eval Contributors
6
6
  License: MIT
@@ -239,7 +239,7 @@ Defines GT modalities, prediction datasets to evaluate, and optional euler_train
239
239
  }
240
240
  ```
241
241
 
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" }`).
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
244
  For sparse pointcloud depth evaluation, use `gt.sparse_depth` instead of `gt.depth`. The prediction still uses `datasets[].depth` because the model output is a dense depth map. 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.
245
245
 
@@ -293,7 +293,7 @@ Dataset metadata (e.g. `radial_depth`, `rgb_range`, sparse point columns, and co
293
293
 
294
294
  ### Dataset Metadata
295
295
 
296
- Each dataset directory must contain ds-crawler metadata artifacts, typically generated under `.ds_crawler/`:
296
+ Each dataset directory or archive must contain ds-crawler metadata artifacts, typically generated under `.ds_crawler/`:
297
297
 
298
298
  ```text
299
299
  dataset-root/
@@ -303,6 +303,8 @@ dataset-root/
303
303
  index.json
304
304
  ```
305
305
 
306
+ When one physical root or archive contains several logical modalities, the artifacts may instead be scoped under `.ds_crawler/<modality>/` with a `.ds_crawler/scopes.json` manifest. `euler-eval` passes modality scopes to euler-loading so paths such as `/data/muses.zip:test` can load `.ds_crawler/rgb/index.json`, `.ds_crawler/depth/index.json`, and related scoped artifacts from the same archive.
307
+
306
308
  GT and prediction datasets are matched by hierarchy path and file ID through `MultiModalDataset`.
307
309
 
308
310
  ### `metrics_config.json`
@@ -201,7 +201,7 @@ Defines GT modalities, prediction datasets to evaluate, and optional euler_train
201
201
  }
202
202
  ```
203
203
 
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" }`).
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
206
  For sparse pointcloud depth evaluation, use `gt.sparse_depth` instead of `gt.depth`. The prediction still uses `datasets[].depth` because the model output is a dense depth map. 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.
207
207
 
@@ -255,7 +255,7 @@ Dataset metadata (e.g. `radial_depth`, `rgb_range`, sparse point columns, and co
255
255
 
256
256
  ### Dataset Metadata
257
257
 
258
- Each dataset directory must contain ds-crawler metadata artifacts, typically generated under `.ds_crawler/`:
258
+ Each dataset directory or archive must contain ds-crawler metadata artifacts, typically generated under `.ds_crawler/`:
259
259
 
260
260
  ```text
261
261
  dataset-root/
@@ -265,6 +265,8 @@ dataset-root/
265
265
  index.json
266
266
  ```
267
267
 
268
+ When one physical root or archive contains several logical modalities, the artifacts may instead be scoped under `.ds_crawler/<modality>/` with a `.ds_crawler/scopes.json` manifest. `euler-eval` passes modality scopes to euler-loading so paths such as `/data/muses.zip:test` can load `.ds_crawler/rgb/index.json`, `.ds_crawler/depth/index.json`, and related scoped artifacts from the same archive.
269
+
268
270
  GT and prediction datasets are matched by hierarchy path and file ID through `MultiModalDataset`.
269
271
 
270
272
  ### `metrics_config.json`
@@ -13,6 +13,7 @@ import zipfile
13
13
  from pathlib import Path
14
14
 
15
15
  import torch
16
+ from euler_loading import Modality
16
17
 
17
18
  from .data import (
18
19
  build_depth_eval_dataset,
@@ -40,6 +41,22 @@ except ImportError:
40
41
  from euler_metric_naming import AxisDeclaration, MetricDescription, MetricNamespace
41
42
 
42
43
 
44
+ def _normalize_modality_path(
45
+ path: str | Path,
46
+ *,
47
+ modality_key: str,
48
+ split: str | None = None,
49
+ ) -> Path:
50
+ """Return the filesystem/archive root for an euler-loading modality path."""
51
+ modality = Modality(
52
+ str(path),
53
+ modality_type=modality_key,
54
+ metadata_scope=modality_key,
55
+ split=split,
56
+ )
57
+ return Path(modality.path)
58
+
59
+
43
60
  # ── Eval namespace ──────────────────────────────────────────────────────────
44
61
  # MetricNamespace subclass for eval context with custom axis declarations,
45
62
  # following the Normed Metric Namespacing convention (§3.6, §4, §5.2).
@@ -599,7 +616,11 @@ def validate_gt_config(gt: dict) -> None:
599
616
  "camera_extrinsics",
600
617
  ):
601
618
  if modality in gt and "path" in gt[modality]:
602
- p = Path(gt[modality]["path"])
619
+ p = _normalize_modality_path(
620
+ gt[modality]["path"],
621
+ modality_key=modality,
622
+ split=gt[modality].get("split"),
623
+ )
603
624
  if not p.exists():
604
625
  raise ValueError(f"gt.{modality}.path does not exist: {p}")
605
626
 
@@ -624,7 +645,11 @@ def validate_dataset_entry(entry: dict, index: int) -> None:
624
645
 
625
646
  for modality in ("rgb", "depth", "rays"):
626
647
  if modality in entry and "path" in entry[modality]:
627
- p = Path(entry[modality]["path"])
648
+ p = _normalize_modality_path(
649
+ entry[modality]["path"],
650
+ modality_key=modality,
651
+ split=entry[modality].get("split"),
652
+ )
628
653
  if not p.exists():
629
654
  raise ValueError(f"{label}.{modality}.path does not exist: {p}")
630
655
 
@@ -749,12 +774,26 @@ def save_results(
749
774
  and modality in dataset_config
750
775
  and "path" in dataset_config[modality]
751
776
  ):
752
- output_file = Path(dataset_config[modality]["path"]) / "eval.json"
777
+ output_file = (
778
+ _normalize_modality_path(
779
+ dataset_config[modality]["path"],
780
+ modality_key=modality,
781
+ split=dataset_config[modality].get("split"),
782
+ )
783
+ / "eval.json"
784
+ )
753
785
  else:
754
786
  # Default: save alongside first available modality path
755
787
  for mod in ("depth", "rgb", "rays"):
756
788
  if mod in dataset_config and "path" in dataset_config[mod]:
757
- output_file = Path(dataset_config[mod]["path"]) / "eval.json"
789
+ output_file = (
790
+ _normalize_modality_path(
791
+ dataset_config[mod]["path"],
792
+ modality_key=mod,
793
+ split=dataset_config[mod].get("split"),
794
+ )
795
+ / "eval.json"
796
+ )
758
797
  break
759
798
  if output_file is None:
760
799
  output_file = Path("eval.json")
@@ -525,13 +525,14 @@ def _resolve_sky_mask_loader(segmentation_path: str) -> Callable[..., Any]:
525
525
  its ``sky_mask`` function so that raw class-segmentation data is
526
526
  converted to a boolean sky mask at load time.
527
527
  """
528
+ modality = _modality(path=segmentation_path, modality_key="segmentation")
528
529
  try:
529
530
  index = index_dataset_from_path(
530
- segmentation_path,
531
- metadata_scope="segmentation",
531
+ modality.path,
532
+ metadata_scope=modality.metadata_scope,
532
533
  )
533
534
  except FileNotFoundError:
534
- index = index_dataset_from_path(segmentation_path)
535
+ index = index_dataset_from_path(modality.path)
535
536
  # Resolve euler_loading metadata the same way euler-loading does:
536
537
  # try the contract addon API first (handles addons.euler_loading),
537
538
  # then fall back to top-level euler_loading for legacy indices.
@@ -546,14 +547,14 @@ def _resolve_sky_mask_loader(segmentation_path: str) -> Callable[..., Any]:
546
547
  loader_name = euler_meta.get("loader")
547
548
  if loader_name is None:
548
549
  raise ValueError(
549
- f"Segmentation dataset at {segmentation_path!r} does not declare "
550
+ f"Segmentation dataset at {modality.path!r} does not declare "
550
551
  f"an 'euler_loading.loader' in its ds-crawler index."
551
552
  )
552
553
  module = resolve_loader_module(loader_name)
553
554
  sky_fn = getattr(module, "sky_mask", None)
554
555
  if sky_fn is None or not callable(sky_fn):
555
556
  raise ValueError(
556
- f"Loader module {loader_name!r} (resolved from {segmentation_path!r}) "
557
+ f"Loader module {loader_name!r} (resolved from {modality.path!r}) "
557
558
  f"does not expose a 'sky_mask' function."
558
559
  )
559
560
  return sky_fn
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: euler-eval
3
- Version: 2.9.0
3
+ Version: 2.10.0
4
4
  Summary: Depth map evaluation toolkit with comprehensive metrics
5
5
  Author: Depth Eval Contributors
6
6
  License: MIT
@@ -239,7 +239,7 @@ Defines GT modalities, prediction datasets to evaluate, and optional euler_train
239
239
  }
240
240
  ```
241
241
 
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" }`).
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
244
  For sparse pointcloud depth evaluation, use `gt.sparse_depth` instead of `gt.depth`. The prediction still uses `datasets[].depth` because the model output is a dense depth map. 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.
245
245
 
@@ -293,7 +293,7 @@ Dataset metadata (e.g. `radial_depth`, `rgb_range`, sparse point columns, and co
293
293
 
294
294
  ### Dataset Metadata
295
295
 
296
- Each dataset directory must contain ds-crawler metadata artifacts, typically generated under `.ds_crawler/`:
296
+ Each dataset directory or archive must contain ds-crawler metadata artifacts, typically generated under `.ds_crawler/`:
297
297
 
298
298
  ```text
299
299
  dataset-root/
@@ -303,6 +303,8 @@ dataset-root/
303
303
  index.json
304
304
  ```
305
305
 
306
+ When one physical root or archive contains several logical modalities, the artifacts may instead be scoped under `.ds_crawler/<modality>/` with a `.ds_crawler/scopes.json` manifest. `euler-eval` passes modality scopes to euler-loading so paths such as `/data/muses.zip:test` can load `.ds_crawler/rgb/index.json`, `.ds_crawler/depth/index.json`, and related scoped artifacts from the same archive.
307
+
306
308
  GT and prediction datasets are matched by hierarchy path and file ID through `MultiModalDataset`.
307
309
 
308
310
  ### `metrics_config.json`
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "euler-eval"
7
- version = "2.9.0"
7
+ version = "2.10.0"
8
8
  description = "Depth map evaluation toolkit with comprehensive metrics"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -1,6 +1,7 @@
1
1
  """Tests for main.py config validation."""
2
2
 
3
3
  import json
4
+ import zipfile
4
5
  import pytest
5
6
 
6
7
  from euler_eval.cli import load_config, validate_dataset_entry, validate_gt_config
@@ -50,6 +51,20 @@ class TestValidateGtConfig:
50
51
  gt = {"rgb": {"path": str(rgb_path)}}
51
52
  validate_gt_config(gt)
52
53
 
54
+ def test_inline_zip_split_gt_path_is_valid(self, tmp_path):
55
+ rgb_path = tmp_path / "reference.zip"
56
+ with zipfile.ZipFile(rgb_path, "w") as zf:
57
+ zf.writestr("dummy.txt", "x")
58
+ gt = {"rgb": {"path": f"{rgb_path}:fog_day"}}
59
+ validate_gt_config(gt)
60
+
61
+ def test_inline_scope_gt_path_is_valid(self, tmp_path):
62
+ rgb_path = tmp_path / "reference.zip"
63
+ with zipfile.ZipFile(rgb_path, "w") as zf:
64
+ zf.writestr("dummy.txt", "x")
65
+ gt = {"rgb": {"path": f"{rgb_path}#scope=rgb"}}
66
+ validate_gt_config(gt)
67
+
53
68
  def test_sparse_depth_requires_projection_modalities(self, tmp_path):
54
69
  sparse_path = tmp_path / "sparse_depth"
55
70
  sparse_path.mkdir()
@@ -132,6 +147,16 @@ class TestValidateDatasetEntry:
132
147
  with pytest.raises(ValueError, match="does not exist"):
133
148
  validate_dataset_entry(entry, 0)
134
149
 
150
+ def test_inline_zip_split_dataset_path_is_valid(self, tmp_path):
151
+ rgb_path = tmp_path / "prediction.zip"
152
+ with zipfile.ZipFile(rgb_path, "w") as zf:
153
+ zf.writestr("dummy.txt", "x")
154
+ entry = {
155
+ "name": "model_a",
156
+ "rgb": {"path": f"{rgb_path}:fog_day"},
157
+ }
158
+ validate_dataset_entry(entry, 0)
159
+
135
160
  def test_index_in_error_message(self, tmp_path):
136
161
  entry = {"name": "test"}
137
162
  with pytest.raises(ValueError, match=r"datasets\[3\]"):
@@ -169,3 +169,26 @@ def test_sky_mask_loader_resolution_uses_segmentation_scope(monkeypatch):
169
169
  assert calls == [
170
170
  ("/datasets/shared", {"metadata_scope": "segmentation"}),
171
171
  ]
172
+
173
+
174
+ def test_sky_mask_loader_resolution_strips_inline_split(monkeypatch):
175
+ calls = []
176
+
177
+ def fake_index_dataset_from_path(path, **kwargs):
178
+ calls.append((path, kwargs))
179
+ return {"euler_loading": {"loader": "vkitti2"}}
180
+
181
+ def sky_mask(path, meta=None):
182
+ return None
183
+
184
+ monkeypatch.setattr(data, "index_dataset_from_path", fake_index_dataset_from_path)
185
+ monkeypatch.setattr(
186
+ data,
187
+ "resolve_loader_module",
188
+ lambda name: SimpleNamespace(sky_mask=sky_mask),
189
+ )
190
+
191
+ assert data._resolve_sky_mask_loader("/datasets/shared.zip:fog_day") is sky_mask
192
+ assert calls == [
193
+ ("/datasets/shared.zip", {"metadata_scope": "segmentation"}),
194
+ ]
@@ -214,6 +214,21 @@ class TestSaveResultsZip:
214
214
  stored = json.loads(zf.read("eval.json"))
215
215
  assert stored == results
216
216
 
217
+ def test_inline_zip_split_writes_eval_json_into_zip(self, tmp_path):
218
+ zp = tmp_path / "dataset.zip"
219
+ with zipfile.ZipFile(zp, "w") as zf:
220
+ zf.writestr("image_001.png", b"\x89PNG")
221
+
222
+ results = {"depth": {"rmse": 0.42}}
223
+ config = {"name": "model", "depth": {"path": f"{zp}:fog_day"}}
224
+ out = save_results(results, config)
225
+
226
+ assert out == zp / "eval.json"
227
+ with zipfile.ZipFile(zp, "r") as zf:
228
+ assert "eval.json" in zf.namelist()
229
+ stored = json.loads(zf.read("eval.json"))
230
+ assert stored == results
231
+
217
232
  def test_preserves_existing_zip_contents(self, tmp_path):
218
233
  zp = tmp_path / "dataset.zip"
219
234
  with zipfile.ZipFile(zp, "w") as zf:
File without changes
File without changes