euler-loading 2.21.0__tar.gz → 2.25.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 (87) hide show
  1. euler_loading-2.25.0/.gitignore +44 -0
  2. euler_loading-2.25.0/CHANGELOG.md +25 -0
  3. euler_loading-2.25.0/LICENSE +21 -0
  4. euler_loading-2.25.0/PKG-INFO +238 -0
  5. euler_loading-2.25.0/README.md +199 -0
  6. euler_loading-2.25.0/docs/README.md +34 -0
  7. euler_loading-2.25.0/docs/dataset.md +328 -0
  8. euler_loading-2.25.0/docs/loaders.md +305 -0
  9. euler_loading-2.25.0/docs/materialization.md +90 -0
  10. euler_loading-2.25.0/docs/preprocessing.md +141 -0
  11. euler_loading-2.25.0/docs/transform-descriptors.md +116 -0
  12. euler_loading-2.25.0/docs/writing.md +125 -0
  13. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/__init__.py +31 -0
  14. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/_dataset_contract.py +2 -0
  15. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/_ds_crawler_utils.py +0 -6
  16. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/_resolution.py +22 -4
  17. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/_writing.py +36 -3
  18. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/dataset.py +136 -23
  19. euler_loading-2.25.0/euler_loading/geometry.py +34 -0
  20. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/__init__.py +14 -12
  21. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/cpu/__init__.py +4 -0
  22. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/cpu/generic_dense_depth.py +0 -1
  23. euler_loading-2.25.0/euler_loading/loaders/cpu/synscapes.py +205 -0
  24. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/generate/loaders.json +126 -0
  25. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/gpu/__init__.py +4 -0
  26. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/gpu/generic_dense_depth.py +0 -1
  27. euler_loading-2.25.0/euler_loading/loaders/gpu/synscapes.py +146 -0
  28. euler_loading-2.25.0/euler_loading/loaders/materialized.py +32 -0
  29. euler_loading-2.25.0/euler_loading/loaders/synscapes.py +7 -0
  30. euler_loading-2.25.0/euler_loading/materialization.py +288 -0
  31. euler_loading-2.25.0/euler_loading/output_encoding.py +138 -0
  32. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/preprocessing.py +19 -46
  33. euler_loading-2.25.0/euler_loading/receipts.py +462 -0
  34. euler_loading-2.25.0/euler_loading/transform_descriptors.py +733 -0
  35. euler_loading-2.25.0/examples/README.md +28 -0
  36. euler_loading-2.25.0/examples/real_drive_sim_preview.py +67 -0
  37. euler_loading-2.25.0/examples/vkitti2_sample.py +104 -0
  38. euler_loading-2.25.0/pyproject.toml +65 -0
  39. {euler_loading-2.21.0 → euler_loading-2.25.0}/tests/test_indexing.py +0 -1
  40. euler_loading-2.25.0/tests/test_materialization.py +577 -0
  41. {euler_loading-2.21.0 → euler_loading-2.25.0}/tests/test_preprocessing.py +0 -1
  42. {euler_loading-2.21.0 → euler_loading-2.25.0}/tests/test_real_dataset.py +37 -16
  43. euler_loading-2.25.0/tests/test_synscapes.py +319 -0
  44. euler_loading-2.25.0/tests/test_transform_descriptors.py +724 -0
  45. {euler_loading-2.21.0 → euler_loading-2.25.0}/tests/test_writing.py +23 -0
  46. euler_loading-2.21.0/.github/workflows/workflow.yml +0 -30
  47. euler_loading-2.21.0/.gitignore +0 -2
  48. euler_loading-2.21.0/PKG-INFO +0 -13
  49. euler_loading-2.21.0/README.md +0 -601
  50. euler_loading-2.21.0/docs/loader-attributes.md +0 -183
  51. euler_loading-2.21.0/example.py +0 -82
  52. euler_loading-2.21.0/gen_loaders.sh +0 -7
  53. euler_loading-2.21.0/package-lock.json +0 -6
  54. euler_loading-2.21.0/pyproject.toml +0 -29
  55. euler_loading-2.21.0/sample_rds.py +0 -30
  56. euler_loading-2.21.0/vkitti_cpu_example_output.json +0 -30
  57. euler_loading-2.21.0/vkitti_gpu_example_output.json +0 -30
  58. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/_metadata.py +0 -0
  59. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/indexing.py +0 -0
  60. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/_annotations.py +0 -0
  61. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/_princeton_dense.py +0 -0
  62. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/_writer_utils.py +0 -0
  63. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/contracts.py +0 -0
  64. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/cpu/generic.py +0 -0
  65. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/cpu/muses.py +0 -0
  66. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/cpu/princeton_dense.py +0 -0
  67. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/cpu/real_drive_sim.py +0 -0
  68. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/cpu/vkitti2.py +0 -0
  69. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/generate/__init__.py +0 -0
  70. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/generate/__main__.py +0 -0
  71. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/generic.py +0 -0
  72. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/gpu/generic.py +0 -0
  73. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/gpu/muses.py +0 -0
  74. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/gpu/princeton_dense.py +0 -0
  75. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/gpu/real_drive_sim.py +0 -0
  76. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/gpu/vkitti2.py +0 -0
  77. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/muses.py +0 -0
  78. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/princeton_dense.py +0 -0
  79. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/real_drive_sim.py +0 -0
  80. {euler_loading-2.21.0 → euler_loading-2.25.0}/euler_loading/loaders/vkitti2.py +0 -0
  81. {euler_loading-2.21.0 → euler_loading-2.25.0}/tests/__init__.py +0 -0
  82. {euler_loading-2.21.0 → euler_loading-2.25.0}/tests/conftest.py +0 -0
  83. {euler_loading-2.21.0 → euler_loading-2.25.0}/tests/example_rds_calib.json +0 -0
  84. {euler_loading-2.21.0 → euler_loading-2.25.0}/tests/test_dataset.py +0 -0
  85. {euler_loading-2.21.0 → euler_loading-2.25.0}/tests/test_id_schema.py +0 -0
  86. {euler_loading-2.21.0 → euler_loading-2.25.0}/tests/test_loaders.py +0 -0
  87. {euler_loading-2.21.0 → euler_loading-2.25.0}/tests/test_python_compat.py +0 -0
@@ -0,0 +1,44 @@
1
+ # Byte-compiled / optimized
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Distribution / packaging
7
+ build/
8
+ dist/
9
+ *.egg-info/
10
+ .eggs/
11
+
12
+ # Virtual environments
13
+ .venv/
14
+ venv/
15
+ env/
16
+
17
+ # Test / coverage artifacts
18
+ .pytest_cache/
19
+ .coverage
20
+ .coverage.*
21
+ htmlcov/
22
+ coverage.xml
23
+ .tox/
24
+ .nox/
25
+
26
+ # Type checkers / linters
27
+ .mypy_cache/
28
+ .ruff_cache/
29
+ .pytype/
30
+
31
+ # Editors and OS cruft
32
+ .vscode/
33
+ .idea/
34
+ .DS_Store
35
+
36
+ # Local agent / assistant scratch (never part of a release)
37
+ .claude/
38
+ .ao-mcp/
39
+ .ao-provider-auth/
40
+ CLAUDE.md
41
+ TASK.md
42
+
43
+ # uv
44
+ uv.lock
@@ -0,0 +1,25 @@
1
+ # Changelog
2
+
3
+ ## 2.24.0 (unreleased)
4
+
5
+ Add source-backed capture, strict per-output writers, explicit NPY/PNG encoding, typed calibration views and replay. Consolidate pinhole geometry and correct legacy skew scaling.
6
+
7
+
8
+ ## 2.23.0 (source changes; not published)
9
+
10
+ - Reject wrapped decoders during dataset export by checking actual built-in
11
+ function identity. Ordinary callable loading remains supported.
12
+ - Apply boolean thresholds before Pillow dtype conversion, and permit exact
13
+ int32/int64 crop and identity-resize plans while refusing float32 resampling.
14
+ - Add `SamplePreprocessor.export_descriptor`, `MultiModalDataset.export_transform_plan`,
15
+ `SerializableTransform`, and `resolve_transform_descriptor`, paired with contract 0.4.0.
16
+ - Freeze profiles, qualified source/calibration bindings, field policies, operation
17
+ order, and installed CPU backend versions. Infer output profiles and virtual
18
+ calibration geometry without materialization claims.
19
+ - Add explicit Torch/Pillow execution, corrected pinhole skew in the opt-in path,
20
+ validity-aware depth policy, source immutability, and worker validator registration.
21
+ - Cover five-field round trips, projected geometry, ambiguous bindings, unsupported
22
+ semantics, backend choices, schemas, and spawned workers in synthetic tests.
23
+
24
+ Legacy preprocessing and writer behavior remain unchanged. Execution receipts,
25
+ materialized metadata propagation, producer wiring, and GT replay are later work.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Daniel Rothenpieler
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,238 @@
1
+ Metadata-Version: 2.5
2
+ Name: euler-loading
3
+ Version: 2.25.0
4
+ Summary: Multi-modal PyTorch dataloader using ds-crawler indices
5
+ Project-URL: Homepage, https://github.com/d-rothen/euler-loading
6
+ Project-URL: Repository, https://github.com/d-rothen/euler-loading
7
+ Project-URL: Issues, https://github.com/d-rothen/euler-loading/issues
8
+ Project-URL: Documentation, https://github.com/d-rothen/euler-loading/tree/main/docs
9
+ Author-email: Daniel Rothenpieler <rothenpielerdaniel@gmail.com>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: computer-vision,dataloader,dataset,depth-estimation,multi-modal,pytorch
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
+ Classifier: Topic :: Scientific/Engineering :: Image Processing
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.9
26
+ Requires-Dist: ds-crawler>=2.11.0
27
+ Requires-Dist: euler-dataset-contract>=0.5.0
28
+ Requires-Dist: numpy
29
+ Requires-Dist: pillow
30
+ Provides-Extra: dev
31
+ Requires-Dist: jsonschema>=4.18; extra == 'dev'
32
+ Requires-Dist: openexr>=3.2; extra == 'dev'
33
+ Requires-Dist: pytest; extra == 'dev'
34
+ Provides-Extra: gpu
35
+ Requires-Dist: torch; extra == 'gpu'
36
+ Provides-Extra: synscapes
37
+ Requires-Dist: openexr>=3.2; extra == 'synscapes'
38
+ Description-Content-Type: text/markdown
39
+
40
+ <!-- euler header — shared across the euler packages.
41
+ Per package, change only: the <h1>, the tagline, and the badge URLs. -->
42
+ <p align="center">
43
+ <img src="https://files.chronodle.com/icons/euler.svg" alt="euler" width="96" height="96">
44
+ </p>
45
+
46
+ <h1 align="center">euler-loading</h1>
47
+
48
+ <p align="center">
49
+ <em>One PyTorch <code>Dataset</code> across arbitrarily many data modalities — matched by ID, not by filename luck.</em>
50
+ </p>
51
+
52
+ <p align="center">
53
+ <a href="https://pypi.org/project/euler-loading/"><img alt="PyPI" src="https://img.shields.io/pypi/v/euler-loading.svg"></a>
54
+ <a href="https://pypi.org/project/euler-loading/"><img alt="Python versions" src="https://img.shields.io/pypi/pyversions/euler-loading.svg"></a>
55
+ <a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-blue.svg"></a>
56
+ <a href="https://github.com/d-rothen/euler-loading/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/d-rothen/euler-loading/actions/workflows/ci.yml/badge.svg"></a>
57
+ </p>
58
+
59
+ ---
60
+
61
+ Multi-modal datasets arrive as separate directory trees — RGB here, depth there,
62
+ segmentation in a zip, calibration somewhere above it all. Keeping them in step
63
+ usually means a pile of fragile path arithmetic.
64
+
65
+ euler-loading replaces that. Each modality is indexed by
66
+ [ds-crawler](https://github.com/d-rothen/ds-crawler), and euler-loading
67
+ **intersects the file IDs** across modalities so every sample holds exactly one
68
+ file per modality. Hierarchical files such as per-scene calibration are matched
69
+ by their position in the tree and shared across the samples below them.
70
+
71
+ ```mermaid
72
+ flowchart LR
73
+ A["rgb/"] --> X
74
+ B["depth/"] --> X
75
+ C["segmentation.zip"] --> X
76
+ X(["intersect file IDs"]) --> S["sample dict<br/>rgb · depth · segmentation<br/>id · full_id · meta · attributes"]
77
+ D["calib.json<br/><i>hierarchical</i>"] -.->|"matched by tree position"| S
78
+ ```
79
+
80
+ It never interprets file contents. It resolves *which* file to load and hands
81
+ the path — or an in-memory buffer, for zip-backed modalities — to a loader
82
+ function, which you supply or let euler-loading resolve from the dataset's
83
+ `dataset-head.json` contract.
84
+
85
+ ## Install
86
+
87
+ ```bash
88
+ pip install "euler-loading[gpu]"
89
+ ```
90
+
91
+ Requires Python 3.9+. The `[gpu]` extra pulls in PyTorch for the tensor loaders;
92
+ without it the package still works using the CPU (NumPy) loaders.
93
+
94
+ For Synscapes EXR depth files, install `euler-loading[synscapes]` (NumPy) or
95
+ `euler-loading[gpu,synscapes]` (tensors). See the
96
+ [Synscapes loaders](docs/loaders.md#synscapes--synscapes) for formats and usage.
97
+
98
+ ## Quick start
99
+
100
+ ```python
101
+ from euler_loading import Modality, MultiModalDataset
102
+ from euler_loading.loaders.gpu import vkitti2
103
+
104
+ dataset = MultiModalDataset(
105
+ modalities={
106
+ "rgb": Modality("/data/vkitti2/rgb", loader=vkitti2.rgb, split="train"),
107
+ "depth": Modality("/data/vkitti2/depth", loader=vkitti2.depth, split="train"),
108
+ },
109
+ hierarchical_modalities={
110
+ "intrinsics": Modality("/data/vkitti2/textgt", loader=vkitti2.read_intrinsics),
111
+ },
112
+ )
113
+
114
+ sample = dataset[0]
115
+ sample["rgb"] # torch.Tensor (3, H, W) float32 in [0, 1]
116
+ sample["depth"] # torch.Tensor (1, H, W) float32, metres
117
+ sample["intrinsics"] # {file_id: (3, 3) tensor} for every calib file above this sample
118
+ sample["id"] # leaf file ID, shared across modalities
119
+ sample["full_id"] # full hierarchical path, e.g. "/Scene01/Camera_0/00000"
120
+ ```
121
+
122
+ Drop it straight into a `DataLoader` — it is a standard `torch.utils.data.Dataset`:
123
+
124
+ ```python
125
+ loader = DataLoader(dataset, batch_size=16, num_workers=4, pin_memory=True)
126
+ ```
127
+
128
+ ## Automatic loader resolution
129
+
130
+ Omit `loader=` and euler-loading resolves the loader declared by that
131
+ modality's ds-crawler dataset contract:
132
+
133
+ ```python
134
+ dataset = MultiModalDataset(
135
+ modalities={
136
+ "rgb": Modality("/data/vkitti2/rgb", split="train"),
137
+ },
138
+ )
139
+ ```
140
+
141
+ The modality root must contain `.ds_crawler/dataset-head.json` (or the scoped
142
+ equivalent) with a named `euler_loading` entry in its `addons` object. A minimal
143
+ RGB contract looks like this:
144
+
145
+ ```json
146
+ {
147
+ "contract": {
148
+ "kind": "dataset_head",
149
+ "version": "1.0"
150
+ },
151
+ "dataset": {
152
+ "id": "vkitti2_rgb",
153
+ "name": "Virtual KITTI 2 RGB"
154
+ },
155
+ "modality": {
156
+ "key": "rgb",
157
+ "meta": {
158
+ "range": [0, 255]
159
+ }
160
+ },
161
+ "addons": {
162
+ "euler_loading": {
163
+ "version": "1.0",
164
+ "loader": "vkitti2",
165
+ "function": "rgb"
166
+ }
167
+ }
168
+ }
169
+ ```
170
+
171
+ `loader` selects a built-in loader module and `function` selects the callable
172
+ inside it. Automatic resolution uses the GPU variant; pass a loader explicitly
173
+ when you want the CPU variant or a custom callable. See
174
+ [Automatic loader resolution](docs/loaders.md#automatic-loader-resolution) for
175
+ the full contract and writer rules.
176
+
177
+ ## What you get
178
+
179
+ | | |
180
+ |---|---|
181
+ | **ID intersection** | Every sample has exactly one file per modality. Unmatched files are reported, not silently dropped. |
182
+ | **Hierarchical modalities** | Per-scene or per-sequence calibration is matched by tree position and cached, with deepest-file-wins inheritance. |
183
+ | **Zip-native** | Point a modality at a `.zip` and files are read from the archive without extraction. One handle per worker. |
184
+ | **Splits** | `Modality(path, split="train")` overlays a ds-crawler inline split on the canonical index. |
185
+ | **Scoped metadata** | Several logical modalities can share one physical root or archive via `metadata_scope`. |
186
+ | **Loader resolution** | Loaders and writers resolve from the `dataset-head.json` `addons.euler_loading` contract, so datasets describe how to read themselves. |
187
+ | **Writing back** | Resolved writers put inference outputs back in dataset-native formats, re-indexable with matching IDs. |
188
+ | **Spatial preprocessing** | `SamplePreprocessor` resizes and crops consistently across images, depth, masks, ray maps *and* intrinsics. |
189
+
190
+ ## Built-in loaders
191
+
192
+ Every dataset module exists in two variants: `loaders.gpu.*` returns
193
+ `torch.Tensor` in CHW layout, `loaders.cpu.*` returns `numpy.ndarray` in HWC.
194
+ All of them accept both filesystem paths and in-memory buffers.
195
+
196
+ | Module | Dataset | Modalities |
197
+ |---|---|---|
198
+ | `vkitti2` | Virtual KITTI 2 | rgb, depth, class/instance segmentation, scene flow, sky mask, intrinsics, extrinsics |
199
+ | `muses` | MUSES | rgb, reference rgb, semantic & panoptic segmentation, sky mask, lidar point cloud, sparse depth, calibration |
200
+ | `real_drive_sim` | Real Drive Sim | rgb, depth, class segmentation, sky mask, calibration, intrinsics, extrinsics |
201
+ | `princeton_dense` | Princeton DENSE / SeeingThroughFog | rgb, rccb, sparse depth, intrinsics, extrinsics |
202
+ | `generic_dense_depth` | *any* — inferred from file extension | rgb, depth, sky mask, intrinsics |
203
+ | `generic` | *any* — `.npy` / `.npz` modalities | points 3d, maps, segmentation, spherical maps, SH coefficients, … |
204
+
205
+ Full inventory with shapes, dtypes and units: [docs/loaders.md](docs/loaders.md).
206
+ The machine-readable version is
207
+ [`loaders.json`](euler_loading/loaders/generate/loaders.json).
208
+
209
+ ## Documentation
210
+
211
+ - [Serializable resize/crop plans](docs/transform-descriptors.md): Phase 1 export,
212
+ explicit backend and calibration bindings, resolution, and output profiles.
213
+
214
+ | Guide | Covers |
215
+ |---|---|
216
+ | [Dataset & modalities](docs/dataset.md) | `Modality` and `MultiModalDataset` reference, the sample dict, splits, scoped metadata, zip archives, layout-aware loading |
217
+ | [Loaders & writers](docs/loaders.md) | The loader contract, automatic resolution, per-file attributes, the full built-in inventory |
218
+ | [Preprocessing & transforms](docs/preprocessing.md) | Cross-modal transforms, `SamplePreprocessor`, calibration-aware resize and crop |
219
+ | [Writing outputs](docs/writing.md) | Writing predictions back in dataset-native formats and re-indexing them |
220
+ | [Examples](examples/) | Runnable scripts against real datasets |
221
+
222
+ ## Development
223
+
224
+ ```bash
225
+ git clone https://github.com/d-rothen/euler-loading.git
226
+ cd euler-loading
227
+ pip install -e ".[gpu,dev]"
228
+ pytest
229
+ ```
230
+
231
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the loader-authoring workflow and
232
+ release process.
233
+
234
+ ## License
235
+
236
+ [MIT](LICENSE) © Daniel Rothenpieler
237
+
238
+ See [Phase 2 materialization](docs/materialization.md) for the opt-in captured spatial workflow (2.24.0, unreleased).
@@ -0,0 +1,199 @@
1
+ <!-- euler header — shared across the euler packages.
2
+ Per package, change only: the <h1>, the tagline, and the badge URLs. -->
3
+ <p align="center">
4
+ <img src="https://files.chronodle.com/icons/euler.svg" alt="euler" width="96" height="96">
5
+ </p>
6
+
7
+ <h1 align="center">euler-loading</h1>
8
+
9
+ <p align="center">
10
+ <em>One PyTorch <code>Dataset</code> across arbitrarily many data modalities — matched by ID, not by filename luck.</em>
11
+ </p>
12
+
13
+ <p align="center">
14
+ <a href="https://pypi.org/project/euler-loading/"><img alt="PyPI" src="https://img.shields.io/pypi/v/euler-loading.svg"></a>
15
+ <a href="https://pypi.org/project/euler-loading/"><img alt="Python versions" src="https://img.shields.io/pypi/pyversions/euler-loading.svg"></a>
16
+ <a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-blue.svg"></a>
17
+ <a href="https://github.com/d-rothen/euler-loading/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/d-rothen/euler-loading/actions/workflows/ci.yml/badge.svg"></a>
18
+ </p>
19
+
20
+ ---
21
+
22
+ Multi-modal datasets arrive as separate directory trees — RGB here, depth there,
23
+ segmentation in a zip, calibration somewhere above it all. Keeping them in step
24
+ usually means a pile of fragile path arithmetic.
25
+
26
+ euler-loading replaces that. Each modality is indexed by
27
+ [ds-crawler](https://github.com/d-rothen/ds-crawler), and euler-loading
28
+ **intersects the file IDs** across modalities so every sample holds exactly one
29
+ file per modality. Hierarchical files such as per-scene calibration are matched
30
+ by their position in the tree and shared across the samples below them.
31
+
32
+ ```mermaid
33
+ flowchart LR
34
+ A["rgb/"] --> X
35
+ B["depth/"] --> X
36
+ C["segmentation.zip"] --> X
37
+ X(["intersect file IDs"]) --> S["sample dict<br/>rgb · depth · segmentation<br/>id · full_id · meta · attributes"]
38
+ D["calib.json<br/><i>hierarchical</i>"] -.->|"matched by tree position"| S
39
+ ```
40
+
41
+ It never interprets file contents. It resolves *which* file to load and hands
42
+ the path — or an in-memory buffer, for zip-backed modalities — to a loader
43
+ function, which you supply or let euler-loading resolve from the dataset's
44
+ `dataset-head.json` contract.
45
+
46
+ ## Install
47
+
48
+ ```bash
49
+ pip install "euler-loading[gpu]"
50
+ ```
51
+
52
+ Requires Python 3.9+. The `[gpu]` extra pulls in PyTorch for the tensor loaders;
53
+ without it the package still works using the CPU (NumPy) loaders.
54
+
55
+ For Synscapes EXR depth files, install `euler-loading[synscapes]` (NumPy) or
56
+ `euler-loading[gpu,synscapes]` (tensors). See the
57
+ [Synscapes loaders](docs/loaders.md#synscapes--synscapes) for formats and usage.
58
+
59
+ ## Quick start
60
+
61
+ ```python
62
+ from euler_loading import Modality, MultiModalDataset
63
+ from euler_loading.loaders.gpu import vkitti2
64
+
65
+ dataset = MultiModalDataset(
66
+ modalities={
67
+ "rgb": Modality("/data/vkitti2/rgb", loader=vkitti2.rgb, split="train"),
68
+ "depth": Modality("/data/vkitti2/depth", loader=vkitti2.depth, split="train"),
69
+ },
70
+ hierarchical_modalities={
71
+ "intrinsics": Modality("/data/vkitti2/textgt", loader=vkitti2.read_intrinsics),
72
+ },
73
+ )
74
+
75
+ sample = dataset[0]
76
+ sample["rgb"] # torch.Tensor (3, H, W) float32 in [0, 1]
77
+ sample["depth"] # torch.Tensor (1, H, W) float32, metres
78
+ sample["intrinsics"] # {file_id: (3, 3) tensor} for every calib file above this sample
79
+ sample["id"] # leaf file ID, shared across modalities
80
+ sample["full_id"] # full hierarchical path, e.g. "/Scene01/Camera_0/00000"
81
+ ```
82
+
83
+ Drop it straight into a `DataLoader` — it is a standard `torch.utils.data.Dataset`:
84
+
85
+ ```python
86
+ loader = DataLoader(dataset, batch_size=16, num_workers=4, pin_memory=True)
87
+ ```
88
+
89
+ ## Automatic loader resolution
90
+
91
+ Omit `loader=` and euler-loading resolves the loader declared by that
92
+ modality's ds-crawler dataset contract:
93
+
94
+ ```python
95
+ dataset = MultiModalDataset(
96
+ modalities={
97
+ "rgb": Modality("/data/vkitti2/rgb", split="train"),
98
+ },
99
+ )
100
+ ```
101
+
102
+ The modality root must contain `.ds_crawler/dataset-head.json` (or the scoped
103
+ equivalent) with a named `euler_loading` entry in its `addons` object. A minimal
104
+ RGB contract looks like this:
105
+
106
+ ```json
107
+ {
108
+ "contract": {
109
+ "kind": "dataset_head",
110
+ "version": "1.0"
111
+ },
112
+ "dataset": {
113
+ "id": "vkitti2_rgb",
114
+ "name": "Virtual KITTI 2 RGB"
115
+ },
116
+ "modality": {
117
+ "key": "rgb",
118
+ "meta": {
119
+ "range": [0, 255]
120
+ }
121
+ },
122
+ "addons": {
123
+ "euler_loading": {
124
+ "version": "1.0",
125
+ "loader": "vkitti2",
126
+ "function": "rgb"
127
+ }
128
+ }
129
+ }
130
+ ```
131
+
132
+ `loader` selects a built-in loader module and `function` selects the callable
133
+ inside it. Automatic resolution uses the GPU variant; pass a loader explicitly
134
+ when you want the CPU variant or a custom callable. See
135
+ [Automatic loader resolution](docs/loaders.md#automatic-loader-resolution) for
136
+ the full contract and writer rules.
137
+
138
+ ## What you get
139
+
140
+ | | |
141
+ |---|---|
142
+ | **ID intersection** | Every sample has exactly one file per modality. Unmatched files are reported, not silently dropped. |
143
+ | **Hierarchical modalities** | Per-scene or per-sequence calibration is matched by tree position and cached, with deepest-file-wins inheritance. |
144
+ | **Zip-native** | Point a modality at a `.zip` and files are read from the archive without extraction. One handle per worker. |
145
+ | **Splits** | `Modality(path, split="train")` overlays a ds-crawler inline split on the canonical index. |
146
+ | **Scoped metadata** | Several logical modalities can share one physical root or archive via `metadata_scope`. |
147
+ | **Loader resolution** | Loaders and writers resolve from the `dataset-head.json` `addons.euler_loading` contract, so datasets describe how to read themselves. |
148
+ | **Writing back** | Resolved writers put inference outputs back in dataset-native formats, re-indexable with matching IDs. |
149
+ | **Spatial preprocessing** | `SamplePreprocessor` resizes and crops consistently across images, depth, masks, ray maps *and* intrinsics. |
150
+
151
+ ## Built-in loaders
152
+
153
+ Every dataset module exists in two variants: `loaders.gpu.*` returns
154
+ `torch.Tensor` in CHW layout, `loaders.cpu.*` returns `numpy.ndarray` in HWC.
155
+ All of them accept both filesystem paths and in-memory buffers.
156
+
157
+ | Module | Dataset | Modalities |
158
+ |---|---|---|
159
+ | `vkitti2` | Virtual KITTI 2 | rgb, depth, class/instance segmentation, scene flow, sky mask, intrinsics, extrinsics |
160
+ | `muses` | MUSES | rgb, reference rgb, semantic & panoptic segmentation, sky mask, lidar point cloud, sparse depth, calibration |
161
+ | `real_drive_sim` | Real Drive Sim | rgb, depth, class segmentation, sky mask, calibration, intrinsics, extrinsics |
162
+ | `princeton_dense` | Princeton DENSE / SeeingThroughFog | rgb, rccb, sparse depth, intrinsics, extrinsics |
163
+ | `generic_dense_depth` | *any* — inferred from file extension | rgb, depth, sky mask, intrinsics |
164
+ | `generic` | *any* — `.npy` / `.npz` modalities | points 3d, maps, segmentation, spherical maps, SH coefficients, … |
165
+
166
+ Full inventory with shapes, dtypes and units: [docs/loaders.md](docs/loaders.md).
167
+ The machine-readable version is
168
+ [`loaders.json`](euler_loading/loaders/generate/loaders.json).
169
+
170
+ ## Documentation
171
+
172
+ - [Serializable resize/crop plans](docs/transform-descriptors.md): Phase 1 export,
173
+ explicit backend and calibration bindings, resolution, and output profiles.
174
+
175
+ | Guide | Covers |
176
+ |---|---|
177
+ | [Dataset & modalities](docs/dataset.md) | `Modality` and `MultiModalDataset` reference, the sample dict, splits, scoped metadata, zip archives, layout-aware loading |
178
+ | [Loaders & writers](docs/loaders.md) | The loader contract, automatic resolution, per-file attributes, the full built-in inventory |
179
+ | [Preprocessing & transforms](docs/preprocessing.md) | Cross-modal transforms, `SamplePreprocessor`, calibration-aware resize and crop |
180
+ | [Writing outputs](docs/writing.md) | Writing predictions back in dataset-native formats and re-indexing them |
181
+ | [Examples](examples/) | Runnable scripts against real datasets |
182
+
183
+ ## Development
184
+
185
+ ```bash
186
+ git clone https://github.com/d-rothen/euler-loading.git
187
+ cd euler-loading
188
+ pip install -e ".[gpu,dev]"
189
+ pytest
190
+ ```
191
+
192
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the loader-authoring workflow and
193
+ release process.
194
+
195
+ ## License
196
+
197
+ [MIT](LICENSE) © Daniel Rothenpieler
198
+
199
+ See [Phase 2 materialization](docs/materialization.md) for the opt-in captured spatial workflow (2.24.0, unreleased).
@@ -0,0 +1,34 @@
1
+ # euler-loading documentation
2
+
3
+ | Guide | Covers |
4
+ |---|---|
5
+ | [Dataset & modalities](dataset.md) | `Modality` and `MultiModalDataset` reference, the sample dict, hierarchical modalities, splits, scoped metadata, zip archives, layout-aware loading |
6
+ | [Loaders & writers](loaders.md) | The loader contract, per-file attributes, automatic resolution, protocols, and the full built-in loader inventory |
7
+ | [Preprocessing & transforms](preprocessing.md) | Cross-modal transforms, `SamplePreprocessor`, field kinds, calibration-aware resize and crop |
8
+ | [Writing outputs](writing.md) | Writing predictions back in dataset-native formats and re-indexing them |
9
+
10
+ Runnable scripts live in [`examples/`](../examples/). Contributor-facing notes —
11
+ adding a loader, running tests, cutting a release — are in
12
+ [CONTRIBUTING.md](../CONTRIBUTING.md).
13
+
14
+ ## Concepts in one page
15
+
16
+ **ds-crawler indexes, euler-loading joins.** Each modality root carries its own
17
+ [ds-crawler](https://github.com/d-rothen/ds-crawler) index describing the files
18
+ it contains and the hierarchy they sit in. euler-loading reads those indexes and
19
+ intersects file IDs so every sample holds one file per modality.
20
+
21
+ **Regular vs hierarchical modalities.** Regular modalities participate in the ID
22
+ intersection — one file each, per sample. Hierarchical modalities do not; their
23
+ files are matched by tree position, so a per-scene calibration file is shared by
24
+ every sample beneath it.
25
+
26
+ **Loaders own all file semantics.** euler-loading never interprets file
27
+ contents. It resolves which file to read and passes a path — or an in-memory
28
+ buffer, for zip-backed modalities — to a loader function.
29
+
30
+ **The dataset-head contract drives resolution.** Each modality can declare an
31
+ `addons.euler_loading` entry in `.ds_crawler/dataset-head.json`. Its `loader`
32
+ and `function` fields select the built-in reader, while the same addon can
33
+ describe writer choice, modality roles and logging slots. See
34
+ [Automatic loader resolution](loaders.md#automatic-loader-resolution).