euler-loading 2.26.0__tar.gz → 2.29.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_loading-2.26.0 → euler_loading-2.29.0}/.gitignore +5 -0
- euler_loading-2.29.0/CHANGELOG.md +100 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/PKG-INFO +37 -3
- {euler_loading-2.26.0 → euler_loading-2.29.0}/README.md +36 -2
- {euler_loading-2.26.0 → euler_loading-2.29.0}/docs/README.md +1 -1
- {euler_loading-2.26.0 → euler_loading-2.29.0}/docs/loaders.md +155 -7
- euler_loading-2.29.0/euler_loading/__main__.py +10 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/_ds_crawler_utils.py +18 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/_resolution.py +63 -12
- euler_loading-2.29.0/euler_loading/cli.py +323 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/dataset.py +27 -8
- euler_loading-2.29.0/euler_loading/dry_run.py +851 -0
- euler_loading-2.29.0/euler_loading/loaders/_sky.py +168 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/cpu/generic.py +23 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/cpu/generic_dense_depth.py +27 -19
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/cpu/muses.py +41 -42
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/cpu/real_drive_sim.py +38 -11
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/cpu/synscapes.py +51 -25
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/cpu/vkitti2.py +26 -15
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/generate/loaders.json +45 -9
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/gpu/generic.py +16 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/gpu/generic_dense_depth.py +7 -41
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/gpu/muses.py +5 -59
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/gpu/real_drive_sim.py +9 -15
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/gpu/synscapes.py +5 -4
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/gpu/vkitti2.py +8 -23
- {euler_loading-2.26.0 → euler_loading-2.29.0}/pyproject.toml +4 -1
- euler_loading-2.29.0/tests/test_dry_run.py +592 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/tests/test_loaders.py +4 -2
- euler_loading-2.29.0/tests/test_sky_class.py +388 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/tests/test_synscapes.py +18 -10
- euler_loading-2.26.0/CHANGELOG.md +0 -43
- {euler_loading-2.26.0 → euler_loading-2.29.0}/LICENSE +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/docs/dataset.md +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/docs/materialization.md +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/docs/preprocessing.md +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/docs/transform-descriptors.md +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/docs/writing.md +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/__init__.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/_dataset_contract.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/_metadata.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/_writing.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/geometry.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/indexing.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/__init__.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/_annotations.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/_princeton_dense.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/_writer_utils.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/contracts.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/cpu/__init__.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/cpu/princeton_dense.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/generate/__init__.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/generate/__main__.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/generic.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/gpu/__init__.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/gpu/princeton_dense.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/materialized.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/muses.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/princeton_dense.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/real_drive_sim.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/synscapes.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/loaders/vkitti2.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/materialization.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/output_encoding.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/preprocessing.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/receipts.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/euler_loading/transform_descriptors.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/examples/README.md +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/examples/real_drive_sim_preview.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/examples/vkitti2_sample.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/tests/__init__.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/tests/conftest.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/tests/example_rds_calib.json +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/tests/test_dataset.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/tests/test_id_schema.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/tests/test_indexing.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/tests/test_materialization.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/tests/test_preprocessing.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/tests/test_python_compat.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/tests/test_real_dataset.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/tests/test_transform_descriptors.py +0 -0
- {euler_loading-2.26.0 → euler_loading-2.29.0}/tests/test_writing.py +0 -0
|
@@ -42,3 +42,8 @@ TASK.md
|
|
|
42
42
|
|
|
43
43
|
# uv
|
|
44
44
|
uv.lock
|
|
45
|
+
|
|
46
|
+
# Stray npm lockfile. Nothing in this package is built with npm, and worktree
|
|
47
|
+
# tooling rewrites its "name" field to the branch directory on every branch,
|
|
48
|
+
# so tracking it produced a merge conflict on every quick-merge.
|
|
49
|
+
package-lock.json
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
- `Modality.function` picks a callable from the loader module the dataset head
|
|
6
|
+
names, instead of its `function`; the writer follows it.
|
|
7
|
+
`Modality(path, function="sky_mask")` reads a `class_segmentation` index as
|
|
8
|
+
boolean sky masks. It is exclusive with `Modality.loader`.
|
|
9
|
+
- The `sky_mask` loaders and writers of `real_drive_sim`, `vkitti2`,
|
|
10
|
+
`synscapes`, `muses` and `generic_dense_depth` read the sky definition from
|
|
11
|
+
the contract field `skyclass` (the sky label in the file's RGB view;
|
|
12
|
+
`[v, v, v]` for a single-channel label): first from the file's attributes,
|
|
13
|
+
then from the dataset head's meta. Their old hard-coded values are now
|
|
14
|
+
defaults, and they log the definition they use once.
|
|
15
|
+
- A `null` `skyclass`, the default from euler-dataset-contract's next release,
|
|
16
|
+
declares no sky: the loaders fall back to a deprecated key or their default,
|
|
17
|
+
and `generic` / `generic_dense_depth` raise. `[0, 0, 0]`, the contract's old
|
|
18
|
+
default, is still taken literally, but a head that declares it logs a warning
|
|
19
|
+
naming it the old default, in place of the warning about differing from the
|
|
20
|
+
loader's default. Loaders without a default warn too.
|
|
21
|
+
- Deprecate the per-loader sky keys `sky_class_id`, `sky_color`, `sky_class`
|
|
22
|
+
and `sky_mask` in favour of `skyclass`. They are still read when no
|
|
23
|
+
`skyclass` is given and raise a `DeprecationWarning`; when `skyclass` is
|
|
24
|
+
also given it wins, and the ignored key is logged as a warning.
|
|
25
|
+
- Warn when the resolved sky differs from a loader's built-in default.
|
|
26
|
+
- Reject values a loader cannot match instead of returning empty masks: a
|
|
27
|
+
non-grey `skyclass` for MUSES label PNGs (as for Synscapes and `generic`),
|
|
28
|
+
and anything but `[id, 0, 0]` for Real Drive Sim.
|
|
29
|
+
- The dry run decodes segmentation heads with their loader module's
|
|
30
|
+
`sky_mask` (at least `SKY_SAMPLES` files), reports the sky fraction, and
|
|
31
|
+
warns when no sampled file has sky or one is more than 90% sky.
|
|
32
|
+
- `real_drive_sim.sky_mask` compares the PNG's RGB value with `skyclass`
|
|
33
|
+
instead of only the red channel; for its own data, where sky is
|
|
34
|
+
`(29, 0, 0)`, the masks are unchanged.
|
|
35
|
+
- Add `generic.sky_mask` for `.npy`/`.npz` class-id maps, which requires
|
|
36
|
+
`skyclass`.
|
|
37
|
+
- The torch `sky_mask` loaders and writers of `real_drive_sim`, `vkitti2`,
|
|
38
|
+
`muses` and `generic_dense_depth` wrap the NumPy ones instead of
|
|
39
|
+
duplicating them.
|
|
40
|
+
- `muses.sky_mask`'s declared default meta is `skyclass: [23, 23, 23]`; the
|
|
41
|
+
old `sky_class: [0, 0, 23]` matched no pixel of a `labelIds` PNG.
|
|
42
|
+
- `MultiModalDataset` logs each modality's effective loader, noting when
|
|
43
|
+
`Modality.function` overrides the index.
|
|
44
|
+
|
|
45
|
+
## 2.27.0 (unreleased)
|
|
46
|
+
|
|
47
|
+
- Add a dry-run command: `euler-loading <path>` (also `python -m
|
|
48
|
+
euler_loading <path>`) walks the ds-crawler artifact sets at or below one
|
|
49
|
+
folder, resolves the loader each dataset head declares in
|
|
50
|
+
`addons.euler_loading`, decodes a sample of the indexed files with it, and
|
|
51
|
+
reports shapes, dtypes and value ranges. Directories and `.zip` archives
|
|
52
|
+
are treated alike, nothing is written, and the exit status is non-zero when
|
|
53
|
+
a modality fails to resolve or decode. `euler_loading.dry_run.dry_run()`
|
|
54
|
+
exposes the same report to Python.
|
|
55
|
+
- `resolve_loader_module` and `resolve_writer_module` take a
|
|
56
|
+
`variant="gpu"|"cpu"` keyword, so the CPU modules can be resolved through
|
|
57
|
+
the same contract pathway as the torch ones. This is what the dry run's
|
|
58
|
+
`--cpu` uses to check archives on a torch-free install.
|
|
59
|
+
|
|
60
|
+
## 2.26.0
|
|
61
|
+
|
|
62
|
+
- Add `read_extrinsics` to the Synscapes loaders, building a 4x4 rigid
|
|
63
|
+
transform from the six `camera.extrinsic` scalars. `transform_direction`
|
|
64
|
+
and `camera_axes` select the pose, its inverse, and vehicle or optical
|
|
65
|
+
camera axes; the assumed `Rz(yaw) @ Ry(pitch) @ Rx(roll)` order is recorded
|
|
66
|
+
in the modality metadata because the dataset does not document it.
|
|
67
|
+
- Add Synscapes writers for every modality, making the loader a
|
|
68
|
+
`DenseDepthCodec`. Depth writes a float32 EXR `Z` channel and needs a
|
|
69
|
+
filesystem path; the intrinsics and extrinsics writers merge into a single
|
|
70
|
+
`meta/<id>.json`, replacing it in one step and keeping its permissions,
|
|
71
|
+
symlink and unmodelled fields rather than truncating it in place.
|
|
72
|
+
- `sky_mask` now honours `meta['sky_class_id']`, or the same key in per-file
|
|
73
|
+
attributes, instead of hardcoding Cityscapes ID 23, so a dataset that
|
|
74
|
+
relabels sky round-trips through `write_sky_mask`.
|
|
75
|
+
- Accept the plural `camera.intrinsics` / `camera.extrinsics` spellings and
|
|
76
|
+
report missing camera fields by name instead of raising `KeyError`.
|
|
77
|
+
|
|
78
|
+
## 2.24.0 (unreleased)
|
|
79
|
+
|
|
80
|
+
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.
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
## 2.23.0 (source changes; not published)
|
|
84
|
+
|
|
85
|
+
- Reject wrapped decoders during dataset export by checking actual built-in
|
|
86
|
+
function identity. Ordinary callable loading remains supported.
|
|
87
|
+
- Apply boolean thresholds before Pillow dtype conversion, and permit exact
|
|
88
|
+
int32/int64 crop and identity-resize plans while refusing float32 resampling.
|
|
89
|
+
- Add `SamplePreprocessor.export_descriptor`, `MultiModalDataset.export_transform_plan`,
|
|
90
|
+
`SerializableTransform`, and `resolve_transform_descriptor`, paired with contract 0.4.0.
|
|
91
|
+
- Freeze profiles, qualified source/calibration bindings, field policies, operation
|
|
92
|
+
order, and installed CPU backend versions. Infer output profiles and virtual
|
|
93
|
+
calibration geometry without materialization claims.
|
|
94
|
+
- Add explicit Torch/Pillow execution, corrected pinhole skew in the opt-in path,
|
|
95
|
+
validity-aware depth policy, source immutability, and worker validator registration.
|
|
96
|
+
- Cover five-field round trips, projected geometry, ambiguous bindings, unsupported
|
|
97
|
+
semantics, backend choices, schemas, and spawned workers in synthetic tests.
|
|
98
|
+
|
|
99
|
+
Legacy preprocessing and writer behavior remain unchanged. Execution receipts,
|
|
100
|
+
materialized metadata propagation, producer wiring, and GT replay are later work.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: euler-loading
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.29.0
|
|
4
4
|
Summary: Multi-modal PyTorch dataloader using ds-crawler indices
|
|
5
5
|
Project-URL: Homepage, https://github.com/d-rothen/euler-loading
|
|
6
6
|
Project-URL: Repository, https://github.com/d-rothen/euler-loading
|
|
@@ -169,11 +169,44 @@ RGB contract looks like this:
|
|
|
169
169
|
```
|
|
170
170
|
|
|
171
171
|
`loader` selects a built-in loader module and `function` selects the callable
|
|
172
|
-
inside it.
|
|
173
|
-
|
|
172
|
+
inside it. `Modality(path, function="sky_mask")` picks another callable from
|
|
173
|
+
the same module, e.g. boolean sky masks from a `class_segmentation` index,
|
|
174
|
+
with the sky taken from the head's `skyclass`. Automatic resolution uses the
|
|
175
|
+
GPU variant; pass a loader explicitly when you want the CPU variant or a
|
|
176
|
+
custom callable. See
|
|
174
177
|
[Automatic loader resolution](docs/loaders.md#automatic-loader-resolution) for
|
|
175
178
|
the full contract and writer rules.
|
|
176
179
|
|
|
180
|
+
## Checking a dataset
|
|
181
|
+
|
|
182
|
+
Before any of that runs, point the bundled command at a folder to see what
|
|
183
|
+
euler-loading would make of it:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
euler-loading /data/vkitti2
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
For every ds-crawler artifact set below that path it resolves the loader the
|
|
190
|
+
dataset declares, decodes a sample of the indexed files with it, and prints
|
|
191
|
+
the shape, dtype and value range that came back:
|
|
192
|
+
|
|
193
|
+
```text
|
|
194
|
+
vkitti_2.0.3_rgb [ok]
|
|
195
|
+
path /data/vkitti2/vkitti_2.0.3_rgb
|
|
196
|
+
contract vkitti2_rgb, "Virtual KITTI 2 RGB", modality key: rgb
|
|
197
|
+
loader vkitti2.rgb -> euler_loading.loaders.gpu.vkitti2.rgb
|
|
198
|
+
index 21260 files; available splits: train, val
|
|
199
|
+
decoded 1/1
|
|
200
|
+
Scene01/clone/frames/rgb/Camera_0/rgb_00000.jpg -> Tensor (3, 375, 1242) float32 in [0, 1] (11.4 ms)
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Nothing is written, directories and `.zip` archives are treated alike, and the
|
|
204
|
+
exit status is non-zero if any modality failed to resolve or decode — so it
|
|
205
|
+
also works as a CI step. `--all` decodes every file instead of a sample,
|
|
206
|
+
`--cpu` checks the NumPy loaders on a torch-free install, and `--json` prints
|
|
207
|
+
the same report for a machine. See
|
|
208
|
+
[Dry-running loaders](docs/loaders.md#dry-running-loaders).
|
|
209
|
+
|
|
177
210
|
## What you get
|
|
178
211
|
|
|
179
212
|
| | |
|
|
@@ -186,6 +219,7 @@ the full contract and writer rules.
|
|
|
186
219
|
| **Loader resolution** | Loaders and writers resolve from the `dataset-head.json` `addons.euler_loading` contract, so datasets describe how to read themselves. |
|
|
187
220
|
| **Writing back** | Resolved writers put inference outputs back in dataset-native formats, re-indexable with matching IDs. |
|
|
188
221
|
| **Spatial preprocessing** | `SamplePreprocessor` resizes and crops consistently across images, depth, masks, ray maps *and* intrinsics. |
|
|
222
|
+
| **Dry runs** | `euler-loading <folder>` resolves and exercises every loader a dataset declares, without writing anything. |
|
|
189
223
|
|
|
190
224
|
## Built-in loaders
|
|
191
225
|
|
|
@@ -130,11 +130,44 @@ RGB contract looks like this:
|
|
|
130
130
|
```
|
|
131
131
|
|
|
132
132
|
`loader` selects a built-in loader module and `function` selects the callable
|
|
133
|
-
inside it.
|
|
134
|
-
|
|
133
|
+
inside it. `Modality(path, function="sky_mask")` picks another callable from
|
|
134
|
+
the same module, e.g. boolean sky masks from a `class_segmentation` index,
|
|
135
|
+
with the sky taken from the head's `skyclass`. Automatic resolution uses the
|
|
136
|
+
GPU variant; pass a loader explicitly when you want the CPU variant or a
|
|
137
|
+
custom callable. See
|
|
135
138
|
[Automatic loader resolution](docs/loaders.md#automatic-loader-resolution) for
|
|
136
139
|
the full contract and writer rules.
|
|
137
140
|
|
|
141
|
+
## Checking a dataset
|
|
142
|
+
|
|
143
|
+
Before any of that runs, point the bundled command at a folder to see what
|
|
144
|
+
euler-loading would make of it:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
euler-loading /data/vkitti2
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
For every ds-crawler artifact set below that path it resolves the loader the
|
|
151
|
+
dataset declares, decodes a sample of the indexed files with it, and prints
|
|
152
|
+
the shape, dtype and value range that came back:
|
|
153
|
+
|
|
154
|
+
```text
|
|
155
|
+
vkitti_2.0.3_rgb [ok]
|
|
156
|
+
path /data/vkitti2/vkitti_2.0.3_rgb
|
|
157
|
+
contract vkitti2_rgb, "Virtual KITTI 2 RGB", modality key: rgb
|
|
158
|
+
loader vkitti2.rgb -> euler_loading.loaders.gpu.vkitti2.rgb
|
|
159
|
+
index 21260 files; available splits: train, val
|
|
160
|
+
decoded 1/1
|
|
161
|
+
Scene01/clone/frames/rgb/Camera_0/rgb_00000.jpg -> Tensor (3, 375, 1242) float32 in [0, 1] (11.4 ms)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Nothing is written, directories and `.zip` archives are treated alike, and the
|
|
165
|
+
exit status is non-zero if any modality failed to resolve or decode — so it
|
|
166
|
+
also works as a CI step. `--all` decodes every file instead of a sample,
|
|
167
|
+
`--cpu` checks the NumPy loaders on a torch-free install, and `--json` prints
|
|
168
|
+
the same report for a machine. See
|
|
169
|
+
[Dry-running loaders](docs/loaders.md#dry-running-loaders).
|
|
170
|
+
|
|
138
171
|
## What you get
|
|
139
172
|
|
|
140
173
|
| | |
|
|
@@ -147,6 +180,7 @@ the full contract and writer rules.
|
|
|
147
180
|
| **Loader resolution** | Loaders and writers resolve from the `dataset-head.json` `addons.euler_loading` contract, so datasets describe how to read themselves. |
|
|
148
181
|
| **Writing back** | Resolved writers put inference outputs back in dataset-native formats, re-indexable with matching IDs. |
|
|
149
182
|
| **Spatial preprocessing** | `SamplePreprocessor` resizes and crops consistently across images, depth, masks, ray maps *and* intrinsics. |
|
|
183
|
+
| **Dry runs** | `euler-loading <folder>` resolves and exercises every loader a dataset declares, without writing anything. |
|
|
150
184
|
|
|
151
185
|
## Built-in loaders
|
|
152
186
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
| Guide | Covers |
|
|
4
4
|
|---|---|
|
|
5
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 |
|
|
6
|
+
| [Loaders & writers](loaders.md) | The loader contract, per-file attributes, automatic resolution, the `euler-loading` dry run, protocols, and the full built-in loader inventory |
|
|
7
7
|
| [Preprocessing & transforms](preprocessing.md) | Cross-modal transforms, `SamplePreprocessor`, field kinds, calibration-aware resize and crop |
|
|
8
8
|
| [Writing outputs](writing.md) | Writing predictions back in dataset-native formats and re-indexing them |
|
|
9
9
|
|
|
@@ -7,6 +7,7 @@ that file means.
|
|
|
7
7
|
- [The contract](#the-contract)
|
|
8
8
|
- [Per-file attributes](#per-file-attributes)
|
|
9
9
|
- [Automatic loader resolution](#automatic-loader-resolution)
|
|
10
|
+
- [Dry-running loaders](#dry-running-loaders)
|
|
10
11
|
- [Loader protocols](#loader-protocols)
|
|
11
12
|
- [Built-in loaders](#built-in-loaders)
|
|
12
13
|
|
|
@@ -119,6 +120,14 @@ An explicit `Modality.loader` always takes precedence. Automatic resolution
|
|
|
119
120
|
uses the GPU variant; import and pass a CPU loader explicitly when NumPy output
|
|
120
121
|
is required.
|
|
121
122
|
|
|
123
|
+
`Modality.function` keeps the head's `loader` module but picks another callable
|
|
124
|
+
in it, and the writer follows it. `Modality(path, function="sky_mask")` reads a
|
|
125
|
+
`class_segmentation` index as boolean sky masks, taking the sky definition from
|
|
126
|
+
the head's `skyclass` (see [Sky masks](#sky-masks)). It cannot be combined with
|
|
127
|
+
`Modality.loader`. Each modality's effective loader is logged when the dataset
|
|
128
|
+
is built, e.g. `loaded with real_drive_sim.sky_mask (the index declares
|
|
129
|
+
'class_segmentation')`.
|
|
130
|
+
|
|
122
131
|
For compatibility, euler-loading can still read the legacy top-level
|
|
123
132
|
`euler_loading` mapping from older indexes. New datasets should declare the
|
|
124
133
|
addon in `dataset-head.json` as shown above.
|
|
@@ -129,6 +138,144 @@ Writers resolve from the same `addons.euler_loading` entry, in this order:
|
|
|
129
138
|
2. for `function: "read_<suffix>"`, `write_<suffix>`,
|
|
130
139
|
3. `write_<function>`.
|
|
131
140
|
|
|
141
|
+
## Dry-running loaders
|
|
142
|
+
|
|
143
|
+
Resolution only pays off if the loader a dataset names can really read its
|
|
144
|
+
files. Point the bundled command at a path to find out, before a training run
|
|
145
|
+
depends on it:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
euler-loading /data/vkitti2
|
|
149
|
+
python -m euler_loading /data/vkitti2 # same thing, no console script needed
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
It takes one path — a modality root, a `.zip` archive, or a folder holding
|
|
153
|
+
several of them — and for every ds-crawler artifact set below it:
|
|
154
|
+
|
|
155
|
+
1. loads the index, or a named split,
|
|
156
|
+
2. resolves the loader its `addons.euler_loading` entry declares,
|
|
157
|
+
3. decodes a sample of the indexed files with that loader,
|
|
158
|
+
4. reports the shape, dtype and value range of what came back.
|
|
159
|
+
|
|
160
|
+
Nothing is written and no dataset is constructed. A modality that cannot be
|
|
161
|
+
read is reported, not raised, so one broken archive does not hide the rest.
|
|
162
|
+
|
|
163
|
+
```text
|
|
164
|
+
euler-loading dry-run: /data/vkitti2
|
|
165
|
+
loader variant: gpu (torch tensors)
|
|
166
|
+
|
|
167
|
+
vkitti_2.0.3_rgb [ok]
|
|
168
|
+
path /data/vkitti2/vkitti_2.0.3_rgb
|
|
169
|
+
contract vkitti2_rgb, "Virtual KITTI 2 RGB", modality key: rgb
|
|
170
|
+
loader vkitti2.rgb -> euler_loading.loaders.gpu.vkitti2.rgb
|
|
171
|
+
writer write_rgb
|
|
172
|
+
index 21260 files; available splits: train, val
|
|
173
|
+
decoded 1/1
|
|
174
|
+
Scene01/clone/frames/rgb/Camera_0/rgb_00000.jpg -> Tensor (3, 375, 1242) float32 in [0, 1] (11.4 ms)
|
|
175
|
+
|
|
176
|
+
vkitti_2.0.3_textgt [ok]
|
|
177
|
+
path /data/vkitti2/vkitti_2.0.3_textgt
|
|
178
|
+
contract vkitti2_intrinsics, "Virtual KITTI 2 intrinsics", modality key: camera_intrinsics
|
|
179
|
+
loader vkitti2.read_intrinsics -> euler_loading.loaders.gpu.vkitti2.read_intrinsics (hierarchical)
|
|
180
|
+
writer write_intrinsics
|
|
181
|
+
index 50 files
|
|
182
|
+
decoded 1/1
|
|
183
|
+
Scene01/clone/intrinsic.txt -> Tensor (3, 3) float32 in [0, 725] (0.6 ms)
|
|
184
|
+
|
|
185
|
+
2 modalities checked: 2 ok, 0 failed; 2/2 files decoded
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The exit status is 0 when every modality resolved a loader and decoded every
|
|
189
|
+
file it was asked to, and 1 otherwise, so the command works as a CI step or a
|
|
190
|
+
job prologue.
|
|
191
|
+
|
|
192
|
+
| Flag | Effect |
|
|
193
|
+
|---|---|
|
|
194
|
+
| `-n, --samples N` | Files to decode per modality, spread evenly across the index (default 1). `0` resolves loaders without decoding anything. |
|
|
195
|
+
| `--all` | Decode every indexed file. Slow, but checks the whole archive. |
|
|
196
|
+
| `--split NAME` | Dry-run a named ds-crawler split instead of the canonical index. |
|
|
197
|
+
| `--scope SCOPE` | Read `.ds_crawler/SCOPE/` only, instead of every artifact set a root carries. |
|
|
198
|
+
| `--cpu` | Resolve the NumPy loaders instead of the torch ones. Works on an install without PyTorch. |
|
|
199
|
+
| `--max-depth N` | Directory levels below the path to search for dataset roots (default 3). |
|
|
200
|
+
| `--json` | Print the report as JSON instead of text. |
|
|
201
|
+
| `-v, --verbose` | Log debug output, including a traceback for every failed load. |
|
|
202
|
+
|
|
203
|
+
The path takes the same inline selectors as `Modality`, so
|
|
204
|
+
`euler-loading /data/muses.zip:train#scope=rgb` checks one split of one scope.
|
|
205
|
+
|
|
206
|
+
Discovery stops at the first dataset root down each branch: a directory
|
|
207
|
+
carrying its own `.ds_crawler/` is checked, never descended into, so a
|
|
208
|
+
dataset's own scene directories are never walked. A root with several
|
|
209
|
+
metadata scopes contributes one entry per scope, labelled with the selector
|
|
210
|
+
that addresses it.
|
|
211
|
+
|
|
212
|
+
### Reading the report
|
|
213
|
+
|
|
214
|
+
| Line | What it is telling you |
|
|
215
|
+
|---|---|
|
|
216
|
+
| `error loader: ... no 'addons.euler_loading' entry` | the dataset head declares no loader, so only an explicit `Modality(..., loader=...)` can read it |
|
|
217
|
+
| `loader vkitti2.rgb (unresolved)` plus an `error` | the contract names a loader that is unknown, misspelled, or needs a dependency this install lacks — `--cpu` resolves the torch-free variant |
|
|
218
|
+
| `decoded 0/2` with per-file `FAILED` | the files are indexed and present, but the declared loader cannot decode them |
|
|
219
|
+
| `(hierarchical)` after the loader | the declared function reads per-scene data; pass that modality as `hierarchical_modalities`, not `modalities` |
|
|
220
|
+
| `error index: ...` | ds-crawler could not produce an index — a missing split, an unreadable archive, or a dataset head the contract rejects |
|
|
221
|
+
| `sky skyclass [29, 0, 0]: sky in 8/8 files, ...` | a segmentation head's files, decoded with its loader module's `sky_mask` (at least `SKY_SAMPLES` = 8 files) |
|
|
222
|
+
| a warning that `sky_mask` finds no sky, or marks a file as more than 90% sky | the head's `skyclass` probably names the wrong label; an `error sky_mask: ...` means the loader rejects it outright |
|
|
223
|
+
| a warning about shared file IDs | the modalities below the path have no file ID in common, so they cannot be intersected into one `MultiModalDataset` |
|
|
224
|
+
|
|
225
|
+
The same report is available as an API, which is what the command is a thin
|
|
226
|
+
shell around:
|
|
227
|
+
|
|
228
|
+
```python
|
|
229
|
+
from euler_loading.dry_run import dry_run
|
|
230
|
+
|
|
231
|
+
report = dry_run("/data/vkitti2", samples=3)
|
|
232
|
+
if not report.ok:
|
|
233
|
+
for modality in report.modalities:
|
|
234
|
+
print(modality.label, modality.errors)
|
|
235
|
+
|
|
236
|
+
report.to_dict() # the same structure --json prints
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Sky masks
|
|
240
|
+
|
|
241
|
+
The dataset contract requires every `semantic_segmentation` head to declare
|
|
242
|
+
`modality.meta.skyclass`: the sky label as it appears in the segmentation file
|
|
243
|
+
viewed as RGB. That is the sky colour of a colour-coded map, and `[v, v, v]`
|
|
244
|
+
for a single-channel label `v`:
|
|
245
|
+
|
|
246
|
+
| Dataset | Encoding | `skyclass` |
|
|
247
|
+
|---|---|---|
|
|
248
|
+
| Virtual KITTI 2 | RGB class colours | `[90, 200, 255]` |
|
|
249
|
+
| Real Drive Sim | class ID in the red channel of an RGBA PNG | `[29, 0, 0]` |
|
|
250
|
+
| Synscapes | single-channel Cityscapes label IDs | `[23, 23, 23]` |
|
|
251
|
+
| MUSES `labelIds` | single-channel Cityscapes label IDs | `[23, 23, 23]` |
|
|
252
|
+
| Cityscapes train IDs, e.g. `generic` `.npy` maps | single-channel | `[10, 10, 10]` |
|
|
253
|
+
|
|
254
|
+
`skyclass: null`, the contract's default, declares no sky. `[0, 0, 0]` is taken
|
|
255
|
+
literally, as label 0 or black; contract releases before `null` used it as the
|
|
256
|
+
default, so heads written then can carry it without anyone having declared it.
|
|
257
|
+
A head that declares `[0, 0, 0]` is therefore logged as a warning.
|
|
258
|
+
|
|
259
|
+
Every `sky_mask` loader (and the matching `write_sky_mask`) resolves the sky in
|
|
260
|
+
this order, skipping a `null` `skyclass`, and logs the definition it uses once:
|
|
261
|
+
|
|
262
|
+
1. `skyclass` in the file's ds-crawler attributes,
|
|
263
|
+
2. `skyclass` in the dataset head's meta,
|
|
264
|
+
3. a deprecated per-loader key — `sky_class_id` (Synscapes, Real Drive Sim,
|
|
265
|
+
MUSES), `sky_color` (VKITTI2), `sky_class` / `sky_mask` (MUSES) or
|
|
266
|
+
`sky_mask` (generic dense depth) — which raises a `DeprecationWarning`,
|
|
267
|
+
4. the loader's built-in default; `generic.sky_mask` and
|
|
268
|
+
`generic_dense_depth.sky_mask` have none and require `skyclass`.
|
|
269
|
+
|
|
270
|
+
A deprecated key that `skyclass` overrides is logged as a warning, saying
|
|
271
|
+
whether it named the same sky. So is a resolved sky that differs from the
|
|
272
|
+
loader's default. Values a loader cannot match raise instead of silently
|
|
273
|
+
finding no sky: a non-grey `skyclass` for single-channel maps (Synscapes,
|
|
274
|
+
MUSES label PNGs, `generic`), and anything but `[id, 0, 0]` for Real Drive Sim,
|
|
275
|
+
which stores class IDs in the red channel. The [dry run](#dry-running-loaders)
|
|
276
|
+
decodes segmentation heads with `sky_mask` and warns about suspicious sky
|
|
277
|
+
fractions.
|
|
278
|
+
|
|
132
279
|
## Loader protocols
|
|
133
280
|
|
|
134
281
|
`DenseDepthLoader` is a `runtime_checkable` Protocol defining the loader
|
|
@@ -179,7 +326,7 @@ annotations on the loader functions themselves.
|
|
|
179
326
|
| `class_segmentation` | CHW / HWC | int64 | RGB-encoded class mask |
|
|
180
327
|
| `instance_segmentation` | CHW / HWC | int64 | RGB-encoded instance mask |
|
|
181
328
|
| `scene_flow` | CHW / HWC | float32 | Optical/scene flow, normalised to `[0, 1]` |
|
|
182
|
-
| `sky_mask` | 1HW / HW | bool |
|
|
329
|
+
| `sky_mask` | 1HW / HW | bool | Head `skyclass` colour in the class mask, default `[90, 200, 255]` |
|
|
183
330
|
| `read_intrinsics` | 3×3 | float32 | From a `.txt` file · *hierarchical* |
|
|
184
331
|
| `read_extrinsics` | N×1 | float32 | From a `.txt` file · *hierarchical* |
|
|
185
332
|
|
|
@@ -193,14 +340,14 @@ Writers exist for every modality above.
|
|
|
193
340
|
| `depth` | 1HW / HW | float32 | `img/depth/<id>.exr`, `Z` channel, planar depth in metres |
|
|
194
341
|
| `class_segmentation` | 1HW / HW | int64 | `img/class/<id>.png`, original Cityscapes label IDs |
|
|
195
342
|
| `instance_segmentation` | 1HW / HW | int64 | `img/instance/<id>.png`, decoded as `R + 256 * G + 65536 * B` |
|
|
196
|
-
| `sky_mask` | 1HW / HW | bool | Sky label in `img/class/<id>.png
|
|
343
|
+
| `sky_mask` | 1HW / HW | bool | Sky label in `img/class/<id>.png`: the grey `skyclass`, ID `23` by default |
|
|
197
344
|
| `read_intrinsics` | 3×3 | float32 | `camera.intrinsic` from `meta/<id>.json`: `fx`, `fy`, `u0`, `v0` |
|
|
198
345
|
| `read_extrinsics` | 4×4 | float32 | `camera.extrinsic` from `meta/<id>.json`: `x`, `y`, `z`, `pitch`, `roll`, `yaw` |
|
|
199
346
|
|
|
200
347
|
Writers exist for every modality above. `write_depth` needs a filesystem path
|
|
201
348
|
because OpenEXR cannot write to a stream; dataset writers hand it a temporary
|
|
202
349
|
file automatically, so zip outputs work unchanged. `write_sky_mask` emits a
|
|
203
|
-
class image labelling sky with `
|
|
350
|
+
class image labelling sky with the label `sky_mask` reads, and
|
|
204
351
|
`write_intrinsics` and `write_extrinsics` merge into one `meta/<id>.json`
|
|
205
352
|
rather than overwriting each other or the file's `scene` and `instance`
|
|
206
353
|
blocks.
|
|
@@ -287,7 +434,7 @@ same resolution. Loaders preserve the stored resolution.
|
|
|
287
434
|
| `reference_rgb` | CHW / HWC | float32 | Clear-weather reference frame, `[0, 1]` |
|
|
288
435
|
| `semantic_segmentation` | 1HW / HW | int64 | Cityscapes `labelIds` or `labelTrainIds` |
|
|
289
436
|
| `semantic_segmentation_color` | CHW / HWC | uint8 | Cityscapes `labelColor` PNG |
|
|
290
|
-
| `sky_mask` | 1HW / HW | bool | `trainId=10
|
|
437
|
+
| `sky_mask` | 1HW / HW | bool | `skyclass` (grey for label PNGs, a colour for RGB class images); default `trainId=10` / `labelId=23` |
|
|
291
438
|
| `panoptic_segmentation` | 1HW / HW | int64 | COCO-style RGB panoptic PNG → segment IDs |
|
|
292
439
|
| `lidar_point_cloud` | (N, 6) | float64 | `.bin`: `x, y, z, intensity, ring, timestamp` |
|
|
293
440
|
| `sparse_depth` | (N, 6) | float64 | Same points, for sparse-depth workflows |
|
|
@@ -303,7 +450,7 @@ same resolution. Loaders preserve the stored resolution.
|
|
|
303
450
|
| `rgb` | CHW / HWC | float32 | PNG, `[0, 1]` |
|
|
304
451
|
| `depth` | 1HW / HW | float32 | `.npz`, metres |
|
|
305
452
|
| `class_segmentation` | 1HW / HW | int64 | Class IDs from the red channel of an RGBA PNG |
|
|
306
|
-
| `sky_mask` | 1HW / HW | bool |
|
|
453
|
+
| `sky_mask` | 1HW / HW | bool | Head `skyclass` colour, default `[29, 0, 0]` (class ID 29) |
|
|
307
454
|
| `calibration` | dict | — | `{sensor: {"K": (3,3), "T": (4,4), "distortion": (8,)}}` · *hierarchical* |
|
|
308
455
|
| `all_intrinsics` | dict | — | `{sensor: (3, 3)}` · *hierarchical* |
|
|
309
456
|
| `read_intrinsics` | 3×3 | float32 | Defaults to `CS_FRONT` · *hierarchical* |
|
|
@@ -330,7 +477,7 @@ Useful for datasets without a dedicated module.
|
|
|
330
477
|
|---|---|---|---|
|
|
331
478
|
| `rgb` | CHW / HWC | float32 | Image (`.png`, `.jpg`, `.bmp`, `.tif`) or NumPy (`.npy`, `.npz`), `[0, 1]` |
|
|
332
479
|
| `depth` | 1HW / HW | float32 | Image or NumPy, returned as-is; honours `attributes["scale_to_meters_override"]` |
|
|
333
|
-
| `sky_mask` | 1HW / HW | bool | Compares pixels against `
|
|
480
|
+
| `sky_mask` | 1HW / HW | bool | Compares pixels against the `skyclass` colour — required |
|
|
334
481
|
| `read_intrinsics` | 3×3 | float32 | Returns `meta["intrinsics"]`, ignores the path — requires `meta` · *hierarchical* |
|
|
335
482
|
|
|
336
483
|
### Generic NumPy modalities — `generic`
|
|
@@ -347,8 +494,9 @@ assumptions beyond shape and dtype.
|
|
|
347
494
|
| `atmospheric_light` | CHW / HWC | float32 |
|
|
348
495
|
| `scattering_coefficient` | HW | float32 |
|
|
349
496
|
| `semantic_segmentation` | HW | uint8 |
|
|
497
|
+
| `sky_mask` | HW | bool (head `skyclass` label, required) |
|
|
350
498
|
| `instance_segmentation` | HW | uint16 |
|
|
351
499
|
| `intrinsics` | 3×3 | float32 |
|
|
352
500
|
| `sh_coeffs` | (N, C) | float32 |
|
|
353
501
|
|
|
354
|
-
Writers exist for every function in this module
|
|
502
|
+
Writers exist for every function in this module except `sky_mask`.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import io
|
|
4
|
+
import zipfile
|
|
3
5
|
from collections.abc import Mapping
|
|
4
6
|
from pathlib import Path
|
|
5
7
|
from typing import Any
|
|
@@ -22,6 +24,22 @@ from ds_crawler.zip_utils import (
|
|
|
22
24
|
)
|
|
23
25
|
|
|
24
26
|
|
|
27
|
+
def read_zip_member(
|
|
28
|
+
archive: zipfile.ZipFile,
|
|
29
|
+
prefix: str,
|
|
30
|
+
relative_path: str,
|
|
31
|
+
) -> io.BytesIO:
|
|
32
|
+
"""Read one member of a zip-backed modality into an in-memory buffer.
|
|
33
|
+
|
|
34
|
+
The buffer carries the modality-relative path as its ``name`` so loaders
|
|
35
|
+
that branch on the file extension behave the same as for a filesystem
|
|
36
|
+
path.
|
|
37
|
+
"""
|
|
38
|
+
buffer = io.BytesIO(archive.read(prefix + relative_path))
|
|
39
|
+
buffer.name = relative_path
|
|
40
|
+
return buffer
|
|
41
|
+
|
|
42
|
+
|
|
25
43
|
def as_non_empty_str(value: Any) -> str | None:
|
|
26
44
|
if value is None:
|
|
27
45
|
return None
|