euler-loading 2.7.0__tar.gz → 2.9.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 (53) hide show
  1. {euler_loading-2.7.0 → euler_loading-2.9.0}/PKG-INFO +1 -1
  2. {euler_loading-2.7.0 → euler_loading-2.9.0}/README.md +34 -85
  3. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/_resolution.py +1 -0
  4. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/_writing.py +4 -3
  5. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/dataset.py +235 -454
  6. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/loaders/__init__.py +4 -0
  7. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/loaders/cpu/__init__.py +2 -0
  8. euler_loading-2.9.0/euler_loading/loaders/cpu/muses.py +180 -0
  9. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/loaders/generate/loaders.json +169 -0
  10. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/loaders/gpu/__init__.py +2 -0
  11. euler_loading-2.9.0/euler_loading/loaders/gpu/muses.py +186 -0
  12. euler_loading-2.9.0/euler_loading/loaders/muses.py +12 -0
  13. {euler_loading-2.7.0 → euler_loading-2.9.0}/package-lock.json +1 -1
  14. {euler_loading-2.7.0 → euler_loading-2.9.0}/pyproject.toml +1 -1
  15. {euler_loading-2.7.0 → euler_loading-2.9.0}/tests/test_dataset.py +58 -478
  16. {euler_loading-2.7.0 → euler_loading-2.9.0}/tests/test_loaders.py +229 -0
  17. {euler_loading-2.7.0 → euler_loading-2.9.0}/.github/workflows/workflow.yml +0 -0
  18. {euler_loading-2.7.0 → euler_loading-2.9.0}/.gitignore +0 -0
  19. {euler_loading-2.7.0 → euler_loading-2.9.0}/docs/loader-attributes.md +0 -0
  20. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/__init__.py +0 -0
  21. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/_dataset_contract.py +0 -0
  22. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/_ds_crawler_utils.py +0 -0
  23. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/_metadata.py +0 -0
  24. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/indexing.py +0 -0
  25. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/loaders/_annotations.py +0 -0
  26. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/loaders/_writer_utils.py +0 -0
  27. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/loaders/contracts.py +0 -0
  28. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/loaders/cpu/generic.py +0 -0
  29. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/loaders/cpu/generic_dense_depth.py +0 -0
  30. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/loaders/cpu/real_drive_sim.py +0 -0
  31. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/loaders/cpu/vkitti2.py +0 -0
  32. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/loaders/generate/__init__.py +0 -0
  33. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/loaders/generate/__main__.py +0 -0
  34. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/loaders/generic.py +0 -0
  35. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/loaders/gpu/generic.py +0 -0
  36. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/loaders/gpu/generic_dense_depth.py +0 -0
  37. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/loaders/gpu/real_drive_sim.py +0 -0
  38. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/loaders/gpu/vkitti2.py +0 -0
  39. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/loaders/real_drive_sim.py +0 -0
  40. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/loaders/vkitti2.py +0 -0
  41. {euler_loading-2.7.0 → euler_loading-2.9.0}/euler_loading/preprocessing.py +0 -0
  42. {euler_loading-2.7.0 → euler_loading-2.9.0}/example.py +0 -0
  43. {euler_loading-2.7.0 → euler_loading-2.9.0}/sample_rds.py +0 -0
  44. {euler_loading-2.7.0 → euler_loading-2.9.0}/tests/__init__.py +0 -0
  45. {euler_loading-2.7.0 → euler_loading-2.9.0}/tests/conftest.py +0 -0
  46. {euler_loading-2.7.0 → euler_loading-2.9.0}/tests/example_rds_calib.json +0 -0
  47. {euler_loading-2.7.0 → euler_loading-2.9.0}/tests/test_id_schema.py +0 -0
  48. {euler_loading-2.7.0 → euler_loading-2.9.0}/tests/test_indexing.py +0 -0
  49. {euler_loading-2.7.0 → euler_loading-2.9.0}/tests/test_preprocessing.py +0 -0
  50. {euler_loading-2.7.0 → euler_loading-2.9.0}/tests/test_real_dataset.py +0 -0
  51. {euler_loading-2.7.0 → euler_loading-2.9.0}/tests/test_writing.py +0 -0
  52. {euler_loading-2.7.0 → euler_loading-2.9.0}/vkitti_cpu_example_output.json +0 -0
  53. {euler_loading-2.7.0 → euler_loading-2.9.0}/vkitti_gpu_example_output.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: euler-loading
3
- Version: 2.7.0
3
+ Version: 2.9.0
4
4
  Summary: Multi-modal PyTorch dataloader using ds-crawler indices
5
5
  Requires-Python: >=3.9
6
6
  Requires-Dist: ds-crawler>=2.3.0
@@ -4,7 +4,7 @@ Multi-modal PyTorch `Dataset` that synchronises files across arbitrary dataset m
4
4
 
5
5
  Each modality points at a directory (or `.zip` archive) that carries its own `ds-crawler.config` (or cached `output.json`).
6
6
  ds-crawler indexes the directory tree, discovers files, and exposes hierarchical metadata (path properties, calibration files, …).
7
- euler-loading then **intersects file IDs** across all modalities so that every sample contains exactly one file per modality. Additional hierarchical data (e.g. per-scene calibration files) can be loaded via `hierarchical_modalities`. For augmentation-style datasets — many augmented files per ground-truth sample — `keyed_modalities` joins each augmented sample to its single GT file via the value encoded in the augmented sample's deepest hierarchy key.
7
+ euler-loading then **intersects file IDs** across all modalities so that every sample contains exactly one file per modality. Additional hierarchical data (e.g. per-scene calibration files) can be loaded via `hierarchical_modalities`. For augmentation-style datasets — many augmented files per ground-truth sample — `euler_layout` metadata lets `MultiModalDataset.from_layout(...)` load shared targets as collapsed hierarchical modalities.
8
8
  How a file is actually **loaded** (image, depth map, point cloud, …) is configurable per modality — either supply a `Callable` or let euler-loading resolve a built-in loader automatically from the ds-crawler config.
9
9
  Writer functions can be resolved the same way, so inference outputs can be written back in dataset-native formats.
10
10
 
@@ -65,8 +65,8 @@ Frozen dataclass describing one data modality.
65
65
  | `hierarchy_scope` | `str \| None` | Optional scope label for hierarchical modalities (e.g. `scene_camera`). |
66
66
  | `applies_to` | `list[str] \| None` | Optional list of regular modality names a hierarchical modality applies to. |
67
67
  | `split` | `str \| None` | Optional inline split name. Loads `.ds_crawler/split_<name>.json` from the modality root (directory or zip) and overlays it on the normal ds-crawler metadata. |
68
- | `keyed_by` | `Mapping[str, str] \| None` | Optional join configuration used only when this modality is passed under `MultiModalDataset(keyed_modalities=...)`. Recognised keys: `key_name` (named-group prefix at the regular modality's deepest hierarchy key, e.g. `"file_id"`; auto-detected from the anchor's data when omitted) and `modality` (anchor regular-modality name; auto-inferred when there's exactly one regular). Both keys are optional the kwarg can be left unset entirely. |
69
- | `cache` | `bool \| None` | Opt-in/out for in-memory caching of decoded values (only meaningful for hierarchical and keyed modalities; regular modalities are never cached). `True` keeps every distinct loaded file in a process-lifetime dict. `False` re-reads on every access. `None` (default): hierarchical `True` (small calibration files benefit), keyed `False` (safe for the augmentation case where the keyed modality holds full per-sample tensors and unbounded caching would OOM). |
68
+ | `cache` | `bool \| None` | Opt-in/out for in-memory caching of decoded values (only meaningful for hierarchical modalities; regular modalities are never cached). `True` keeps every distinct loaded file in a process-lifetime dict. `False` re-reads on every access. `None` (default): hierarchical `True` because small shared calibration files benefit. |
69
+ | `collapse_single` | `bool` | For hierarchical modalities only. When `True`, a sample that matches exactly one hierarchical file returns that loaded value directly instead of `{file_id: value}`. This is useful for shared GT tensors used by multiple augmentations of the same source sample. |
70
70
  | `metadata` | `dict[str, Any]` | Optional arbitrary metadata. Keys under `metadata["euler_loading"]` are treated as euler-loading defaults. |
71
71
 
72
72
  The loader is the **only** place where domain-specific I/O happens.
@@ -137,30 +137,23 @@ Returns a dict mapping each regular modality name to `{"path": ..., "origin_path
137
137
 
138
138
  Returns a dict mapping each hierarchical modality name to `{"path": ..., "origin_path": ...}` and includes `split` when configured.
139
139
 
140
- ### `MultiModalDataset.keyed_modality_paths()`
141
-
142
- Returns a dict mapping each keyed modality name to `{"path": ..., "origin_path": ...}` plus `keyed_by_modality` (the resolved anchor) and `keyed_by_key_name` (the resolved or auto-detected key name). Includes `split` when configured. Useful for verifying which prefix the auto-detection picked.
143
-
144
140
  ### `MultiModalDataset.get_modality_metadata(modality_name)`
145
141
 
146
142
  Returns the ds-crawler metadata dict for the given modality.
147
143
 
148
- ### `MultiModalDataset(modalities, hierarchical_modalities=None, transforms=None, keyed_modalities=None, strict_keyed=False)`
144
+ ### `MultiModalDataset(modalities, hierarchical_modalities=None, transforms=None)`
149
145
 
150
146
  PyTorch `Dataset`. On construction it:
151
147
 
152
- 1. Runs `ds_crawler.index_dataset_from_path()` for every modality (regular, hierarchical, and keyed).
148
+ 1. Runs `ds_crawler.index_dataset_from_path()` for every modality (regular and hierarchical).
153
149
  2. Computes the **sorted intersection** of file IDs across all regular modalities.
154
- 3. Validates keyed-modality joins for every common id; drops samples whose join misses (or raises when `strict_keyed=True`).
155
- 4. Logs warnings for unmatched files; raises `ValueError` when the intersection is empty or all samples are dropped by keyed-join validation.
150
+ 3. Logs warnings for unmatched files; raises `ValueError` when the intersection is empty.
156
151
 
157
152
  | Parameter | Type | Description |
158
153
  |-----------|------|-------------|
159
154
  | `modalities` | `dict[str, Modality]` | At least one entry required. Keys become the sample dict keys. These modalities participate in ID intersection. |
160
155
  | `hierarchical_modalities` | `dict[str, Modality] \| None` | Optional modalities whose files live at intermediate hierarchy levels (e.g. per-scene intrinsics). These do **not** participate in ID intersection. Each sample will contain a dict `{file_id: loaded_result}` with all files at or above the sample's hierarchy level. Results are cached so shared files are parsed only once. |
161
- | `keyed_modalities` | `dict[str, Modality] \| None` | Optional modalities joined to a regular sample by the value of the regular sample's deepest hierarchy key. See [Keyed modalities](#keyed-modalities) below. Contributes a single loaded value per sample (not a dict). Caching is **off by default** for keyed modalities (per-sample tensors can be large); set `Modality(..., cache=True)` to opt in when the keyed dataset is small enough to fit in memory. |
162
156
  | `transforms` | `list[Callable[[dict], dict]] \| None` | Applied in order after loading. Each receives and returns the full sample dict. |
163
- | `strict_keyed` | `bool` | When `True`, missing or mis-decoded keyed-modality joins raise immediately at construction instead of warning + dropping the affected samples. Default `False`. |
164
157
 
165
158
  #### Sample dict
166
159
 
@@ -175,8 +168,6 @@ PyTorch `Dataset`. On construction it:
175
168
  ...
176
169
  },
177
170
  ...
178
- "<keyed_modality_name>": <loader result>, # one entry per keyed modality (single value)
179
- ...
180
171
  "id": str, # file ID (leaf only, shared across modalities)
181
172
  "full_id": str, # full hierarchical path including file ID (e.g. "/scene/camera/frame")
182
173
  "meta": { # per-modality ds-crawler file entries
@@ -184,7 +175,7 @@ PyTorch `Dataset`. On construction it:
184
175
  ...
185
176
  },
186
177
  "attributes": { # per-modality top-level surface for file_entry["attributes"]
187
- "<modality_name>": {...}, # for regular and keyed modalities (single dict)
178
+ "<modality_name>": {...}, # for regular modalities (single dict)
188
179
  "<hierarchical_modality_name>": { # for hierarchical modalities (one dict per matched file)
189
180
  "<file_id>": {...},
190
181
  ...
@@ -194,7 +185,7 @@ PyTorch `Dataset`. On construction it:
194
185
  }
195
186
  ```
196
187
 
197
- Hierarchical modality results are cached by default; keyed modality results are not. Both can be flipped via `Modality(..., cache=True|False)` — see the [`cache` row in the `Modality` table](#modalitypath--loadernone-metadatanone) and [Keyed modalities](#keyed-modalities) for details on when each default is the right choice.
188
+ Hierarchical modality results are cached by default. This can be flipped via `Modality(..., cache=True|False)` — see the [`cache` row in the `Modality` table](#modalitypath--loadernone-metadatanone).
198
189
 
199
190
  ### `FileRecord`
200
191
 
@@ -318,7 +309,7 @@ When `Modality.loader` is `None`, euler-loading resolves the loader from the ds-
318
309
  }
319
310
  ```
320
311
 
321
- `loader` is the module name (`vkitti2`, `real_drive_sim`, or `generic_dense_depth`) and `function` is the function within that module. The GPU variant is used by default.
312
+ `loader` is the module name (`vkitti2`, `real_drive_sim`, `muses`, or `generic_dense_depth`) and `function` is the function within that module. The GPU variant is used by default.
322
313
 
323
314
  Writer resolution uses the same module and function metadata:
324
315
 
@@ -334,13 +325,9 @@ Files across modalities are matched by these IDs, so **the directory structure m
334
325
 
335
326
  Calibration files or other per-scene/per-sequence metadata can be loaded via `hierarchical_modalities`. These files are matched to samples based on their position in the hierarchy — all files at or above a sample's hierarchy level are included and cached for efficiency.
336
327
 
337
- ## Keyed modalities
338
-
339
- A **keyed modality** joins each regular sample to a single file in another dataset by reading the *value* of the regular sample's deepest hierarchy key. This is the right tool when one modality augments samples of another at a different hierarchy depth — typically: many augmented files per ground-truth sample.
340
-
341
- ### When to use it
328
+ ## Layout-aware augmentation loading
342
329
 
343
- Layout where the augmented modality nests files under an extra `file_id:<id>` level, while the GT keeps the file id as the filename stem:
330
+ Augmentation datasets often contain many files for the same source sample, while a target modality such as GT depth should be reused for every augmentation of that source. The recommended layout is to index the source sample as a hierarchy level and the augmentation variant as the file ID:
344
331
 
345
332
  ```
346
333
  augmented_rgb/
@@ -349,82 +336,32 @@ augmented_rgb/
349
336
  scene_000000/CS_FRONT/file_id:000000000000000026/mor_10m.png
350
337
  ...
351
338
  gt_depth/
352
- scene_000000/CS_FRONT/000000000000000025.png
353
- scene_000000/CS_FRONT/000000000000000026.png
339
+ scene_000000/CS_FRONT/file_id:000000000000000025/depth.png
340
+ scene_000000/CS_FRONT/file_id:000000000000000026/depth.png
354
341
  ...
355
342
  ```
356
343
 
357
- ds-crawler indexes both layouts unchanged; the augmented modality's `indexing.hierarchy.separator` must be `":"` (or any single separator) so the deepest key `"file_id:000…025"` decodes into `("file_id", "000…025")`.
358
-
359
- ### Wiring
344
+ Both modality indexes should include an `euler_layout` addon. The augmented RGB layout declares a `sample_axis` such as `file_id` at `location="hierarchy"` and a `variant_axis` at `location="file_id"`. The GT depth layout declares the same `sample_axis` and no variant axis.
360
345
 
361
346
  ```python
362
347
  from euler_loading import Modality, MultiModalDataset
363
348
 
364
- dataset = MultiModalDataset(
365
- modalities={
349
+ dataset = MultiModalDataset.from_layout(
350
+ {
366
351
  "rgb_aug": Modality("/data/augmented_rgb", loader=load_rgb),
367
- },
368
- keyed_modalities={
369
352
  "depth": Modality("/data/gt_depth", loader=load_depth),
370
353
  },
354
+ primary="rgb_aug",
371
355
  )
372
356
 
373
357
  sample = dataset[0]
374
- # sample["rgb_aug"] – the per-aug RGB
375
- # sample["depth"] – the GT depth for this file_id (single value, shared across augs)
358
+ # sample["rgb_aug"] – the per-augmentation RGB
359
+ # sample["depth"] – the GT depth for this file_id, shared across variants
376
360
  # sample["full_id"] – e.g. "/scene_000000/CS_FRONT/file_id:000…025/mor_10m"
377
- # sample["id"] – the augmentation's leaf id (e.g. "mor_10m")
361
+ # sample["id"] – the augmentation's leaf id, e.g. "mor_10m"
378
362
  ```
379
363
 
380
- `keyed_by` is optional both `key_name` (the named-group prefix at the regular modality's deepest hierarchy key) and `modality` (the anchor regular modality) are auto-detected when unambiguous. Set them explicitly when:
381
-
382
- - there are multiple regular modalities (anchor must be picked: `keyed_by={"modality": "rgb_aug", "key_name": "file_id"}`); or
383
- - the anchor's deepest hierarchy keys mix multiple prefixes (e.g. some `file_id:…` and some `frame:…`).
384
-
385
- ### Sample shape
386
-
387
- A keyed modality contributes a **single loaded value** per sample, unlike hierarchical modalities which return `{file_id: loader_result}`. The join returns exactly one record by construction (one GT per augmented sample's file id at the parent hierarchy prefix), so a dict would be misleading.
388
-
389
- ### How the join works
390
-
391
- For each common id in the regular modalities, euler-loading:
392
-
393
- 1. Looks up the anchor record's `hierarchy_path`, e.g. `(scene_000000, CS_FRONT, file_id:000…025)`.
394
- 2. Splits the deepest key on the regular modality's `indexing.hierarchy.separator` → `("file_id", "000…025")`.
395
- 3. Verifies the key name matches the configured (or auto-detected) `key_name`.
396
- 4. Looks up the keyed modality's record at hierarchy `(scene_000000, CS_FRONT)` with `id == "000…025"`.
397
-
398
- ### Caching
399
-
400
- Caching for keyed modalities is **off by default**, which differs from hierarchical modalities (default on). The reason is workload shape: hierarchical files are usually tiny calibration / metadata blobs that you'd happily keep in memory forever, whereas a keyed modality typically holds the same kind of full per-sample tensor as a regular modality. Caching every decoded GT depth tensor in a 500 GB dataset would OOM the process.
401
-
402
- Override per modality:
403
-
404
- ```python
405
- keyed_modalities={
406
- # Small GT (a few hundred MB total): worth caching to avoid re-reads
407
- # across augmentations of the same file_id.
408
- "depth": Modality("/data/gt_depth", loader=load_depth, cache=True),
409
- # Large GT (multi-hundred-GB total): keep it off, re-read each access.
410
- "lidar": Modality("/data/gt_lidar", loader=load_lidar), # cache=False default
411
- }
412
- ```
413
-
414
- When `cache=True`, the GT for `file_id:000…025` is decoded once and reused across every augmentation that points at it. When `cache=False` (the default), every sample access re-reads and re-decodes the file.
415
-
416
- ### Validation and missing joins
417
-
418
- Construction-time validation runs the decoding for every common id. Samples whose decode or lookup fails are dropped from the dataset with a per-modality warning summarising the count. Pass `strict_keyed=True` to raise instead — useful in pipelines where dropped samples should be a hard error. Common error cases (with precise messages):
419
-
420
- - key-name mismatch (deepest key starts with a different prefix);
421
- - regular modality with no `indexing.hierarchy.separator`;
422
- - ambiguous auto-detection (multiple distinct deepest-key prefixes in the anchor);
423
- - regular modality with no hierarchy at all.
424
-
425
- ### Writing keyed-modality outputs
426
-
427
- `MultiModalDataset.write_sample(...)` accepts keyed modality names alongside regular ones. The destination path is derived from the keyed record (the GT's relative path), so prediction outputs land in the GT-shape layout — *not* under a synthetic `file_id:` subdirectory.
364
+ `from_layout(...)` plans the GT modality as `hierarchical_modalities={"depth": Modality(..., collapse_single=True)}`. The hierarchical match uses the regular sample's hierarchy path, so source samples remain disambiguated by their parent hierarchy as well as by `file_id`. If a shared modality declares the same `sample_axis` but its index does not expose that axis as hierarchy, construction raises and the dataset should be re-indexed with the source sample axis in hierarchy.
428
365
 
429
366
  ## DenseDepthLoader protocol
430
367
 
@@ -502,6 +439,18 @@ All built-in loaders accept both filesystem paths (`str`) and in-memory buffers
502
439
  | `sky_mask` | Binary mask where class ID == 29 (sky) |
503
440
  | `calibration` | Per-sensor calibration from JSON: returns `dict[sensor_name, {"K": (3,3), "T": (4,4), "distortion": (8,)}]` (use with `hierarchical_modalities`) |
504
441
 
442
+ ### MUSES (`euler_loading.loaders.muses`)
443
+
444
+ | Function | Description |
445
+ |----------|-------------|
446
+ | `rgb` | Frame-camera RGB image as float32, normalised to [0, 1] |
447
+ | `reference_rgb` | Clear-weather reference RGB image as float32, normalised to [0, 1] |
448
+ | `semantic_segmentation` | Single-channel Cityscapes `labelIds` or `labelTrainIds` PNG as class IDs |
449
+ | `semantic_segmentation_color` | Cityscapes `labelColor` PNG as RGB uint8 labels |
450
+ | `panoptic_segmentation` | COCO-style RGB panoptic PNG decoded to integer segment IDs |
451
+ | `lidar_point_cloud` / `point_cloud` | Lidar `.bin` file as `(N, 6)` float64: `x, y, z, intensity, ring, timestamp` |
452
+ | `sparse_depth` | Alias for MUSES lidar points for sparse-depth style workflows |
453
+
505
454
  ### Generic Dense Depth (`euler_loading.loaders.gpu.generic_dense_depth`)
506
455
 
507
456
  A format-agnostic loader that infers the loading strategy from the file extension. Useful for datasets that don't have a dedicated loader module.
@@ -513,7 +462,7 @@ A format-agnostic loader that infers the loading strategy from the file extensio
513
462
  | `sky_mask` | Binary mask by comparing pixels against `meta["sky_mask"]` (`[R, G, B]`). Requires `meta` |
514
463
  | `read_intrinsics` | Returns `meta["intrinsics"]` as a `(3, 3)` tensor. Ignores path; requires `meta` |
515
464
 
516
- CPU variants of all loaders live under `euler_loading.loaders.cpu.{vkitti2,real_drive_sim,generic_dense_depth}`.
465
+ CPU variants of all loaders live under `euler_loading.loaders.cpu.{vkitti2,real_drive_sim,muses,generic_dense_depth}`.
517
466
 
518
467
  ### Flattening hierarchical modalities
519
468
 
@@ -23,6 +23,7 @@ _LOADER_MODULES: dict[str, str] = {
23
23
  "real_drive_sim": "euler_loading.loaders.gpu.real_drive_sim",
24
24
  "generic_dense_depth": "euler_loading.loaders.gpu.generic_dense_depth",
25
25
  "generic": "euler_loading.loaders.gpu.generic",
26
+ "muses": "euler_loading.loaders.gpu.muses",
26
27
  }
27
28
 
28
29
 
@@ -55,10 +55,11 @@ def _wrap_writer_save_index(
55
55
  result["meta"] = dict(meta)
56
56
 
57
57
  if isinstance(addons, Mapping):
58
- for addon_name in ("euler_train", "euler_loading"):
59
- addon = addons.get(addon_name)
58
+ for addon_name, addon in addons.items():
59
+ if not str(addon_name).startswith("euler_"):
60
+ continue
60
61
  if isinstance(addon, Mapping):
61
- result[addon_name] = dict(addon)
62
+ result[str(addon_name)] = dict(addon)
62
63
 
63
64
  index_tree = output.get("index")
64
65
  if isinstance(index_tree, Mapping):