euler-loading 2.28.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.28.0 → euler_loading-2.29.0}/CHANGELOG.md +42 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/PKG-INFO +6 -3
- {euler_loading-2.28.0 → euler_loading-2.29.0}/README.md +5 -2
- {euler_loading-2.28.0 → euler_loading-2.29.0}/docs/loaders.md +58 -7
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/_resolution.py +32 -6
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/cli.py +3 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/dataset.py +21 -2
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/dry_run.py +186 -58
- euler_loading-2.29.0/euler_loading/loaders/_sky.py +168 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/cpu/generic.py +23 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/cpu/generic_dense_depth.py +27 -19
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/cpu/muses.py +41 -42
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/cpu/real_drive_sim.py +38 -11
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/cpu/synscapes.py +51 -25
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/cpu/vkitti2.py +26 -15
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/generate/loaders.json +45 -9
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/gpu/generic.py +16 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/gpu/generic_dense_depth.py +7 -41
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/gpu/muses.py +5 -59
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/gpu/real_drive_sim.py +9 -15
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/gpu/synscapes.py +5 -4
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/gpu/vkitti2.py +8 -23
- {euler_loading-2.28.0 → euler_loading-2.29.0}/pyproject.toml +1 -1
- {euler_loading-2.28.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.28.0 → euler_loading-2.29.0}/tests/test_synscapes.py +18 -10
- {euler_loading-2.28.0 → euler_loading-2.29.0}/.gitignore +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/LICENSE +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/docs/README.md +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/docs/dataset.md +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/docs/materialization.md +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/docs/preprocessing.md +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/docs/transform-descriptors.md +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/docs/writing.md +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/__init__.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/__main__.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/_dataset_contract.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/_ds_crawler_utils.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/_metadata.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/_writing.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/geometry.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/indexing.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/__init__.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/_annotations.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/_princeton_dense.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/_writer_utils.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/contracts.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/cpu/__init__.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/cpu/princeton_dense.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/generate/__init__.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/generate/__main__.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/generic.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/gpu/__init__.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/gpu/princeton_dense.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/materialized.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/muses.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/princeton_dense.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/real_drive_sim.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/synscapes.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/loaders/vkitti2.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/materialization.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/output_encoding.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/preprocessing.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/receipts.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/euler_loading/transform_descriptors.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/examples/README.md +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/examples/real_drive_sim_preview.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/examples/vkitti2_sample.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/tests/__init__.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/tests/conftest.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/tests/example_rds_calib.json +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/tests/test_dataset.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/tests/test_dry_run.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/tests/test_id_schema.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/tests/test_indexing.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/tests/test_materialization.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/tests/test_preprocessing.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/tests/test_python_compat.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/tests/test_real_dataset.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/tests/test_transform_descriptors.py +0 -0
- {euler_loading-2.28.0 → euler_loading-2.29.0}/tests/test_writing.py +0 -0
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# Changelog
|
|
2
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
|
+
|
|
3
45
|
## 2.27.0 (unreleased)
|
|
4
46
|
|
|
5
47
|
- Add a dry-run command: `euler-loading <path>` (also `python -m
|
|
@@ -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,8 +169,11 @@ 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
|
|
|
@@ -130,8 +130,11 @@ 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
|
|
|
@@ -120,6 +120,14 @@ An explicit `Modality.loader` always takes precedence. Automatic resolution
|
|
|
120
120
|
uses the GPU variant; import and pass a CPU loader explicitly when NumPy output
|
|
121
121
|
is required.
|
|
122
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
|
+
|
|
123
131
|
For compatibility, euler-loading can still read the legacy top-level
|
|
124
132
|
`euler_loading` mapping from older indexes. New datasets should declare the
|
|
125
133
|
addon in `dataset-head.json` as shown above.
|
|
@@ -210,6 +218,8 @@ that addresses it.
|
|
|
210
218
|
| `decoded 0/2` with per-file `FAILED` | the files are indexed and present, but the declared loader cannot decode them |
|
|
211
219
|
| `(hierarchical)` after the loader | the declared function reads per-scene data; pass that modality as `hierarchical_modalities`, not `modalities` |
|
|
212
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 |
|
|
213
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` |
|
|
214
224
|
|
|
215
225
|
The same report is available as an API, which is what the command is a thin
|
|
@@ -226,6 +236,46 @@ if not report.ok:
|
|
|
226
236
|
report.to_dict() # the same structure --json prints
|
|
227
237
|
```
|
|
228
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
|
+
|
|
229
279
|
## Loader protocols
|
|
230
280
|
|
|
231
281
|
`DenseDepthLoader` is a `runtime_checkable` Protocol defining the loader
|
|
@@ -276,7 +326,7 @@ annotations on the loader functions themselves.
|
|
|
276
326
|
| `class_segmentation` | CHW / HWC | int64 | RGB-encoded class mask |
|
|
277
327
|
| `instance_segmentation` | CHW / HWC | int64 | RGB-encoded instance mask |
|
|
278
328
|
| `scene_flow` | CHW / HWC | float32 | Optical/scene flow, normalised to `[0, 1]` |
|
|
279
|
-
| `sky_mask` | 1HW / HW | bool |
|
|
329
|
+
| `sky_mask` | 1HW / HW | bool | Head `skyclass` colour in the class mask, default `[90, 200, 255]` |
|
|
280
330
|
| `read_intrinsics` | 3×3 | float32 | From a `.txt` file · *hierarchical* |
|
|
281
331
|
| `read_extrinsics` | N×1 | float32 | From a `.txt` file · *hierarchical* |
|
|
282
332
|
|
|
@@ -290,14 +340,14 @@ Writers exist for every modality above.
|
|
|
290
340
|
| `depth` | 1HW / HW | float32 | `img/depth/<id>.exr`, `Z` channel, planar depth in metres |
|
|
291
341
|
| `class_segmentation` | 1HW / HW | int64 | `img/class/<id>.png`, original Cityscapes label IDs |
|
|
292
342
|
| `instance_segmentation` | 1HW / HW | int64 | `img/instance/<id>.png`, decoded as `R + 256 * G + 65536 * B` |
|
|
293
|
-
| `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 |
|
|
294
344
|
| `read_intrinsics` | 3×3 | float32 | `camera.intrinsic` from `meta/<id>.json`: `fx`, `fy`, `u0`, `v0` |
|
|
295
345
|
| `read_extrinsics` | 4×4 | float32 | `camera.extrinsic` from `meta/<id>.json`: `x`, `y`, `z`, `pitch`, `roll`, `yaw` |
|
|
296
346
|
|
|
297
347
|
Writers exist for every modality above. `write_depth` needs a filesystem path
|
|
298
348
|
because OpenEXR cannot write to a stream; dataset writers hand it a temporary
|
|
299
349
|
file automatically, so zip outputs work unchanged. `write_sky_mask` emits a
|
|
300
|
-
class image labelling sky with `
|
|
350
|
+
class image labelling sky with the label `sky_mask` reads, and
|
|
301
351
|
`write_intrinsics` and `write_extrinsics` merge into one `meta/<id>.json`
|
|
302
352
|
rather than overwriting each other or the file's `scene` and `instance`
|
|
303
353
|
blocks.
|
|
@@ -384,7 +434,7 @@ same resolution. Loaders preserve the stored resolution.
|
|
|
384
434
|
| `reference_rgb` | CHW / HWC | float32 | Clear-weather reference frame, `[0, 1]` |
|
|
385
435
|
| `semantic_segmentation` | 1HW / HW | int64 | Cityscapes `labelIds` or `labelTrainIds` |
|
|
386
436
|
| `semantic_segmentation_color` | CHW / HWC | uint8 | Cityscapes `labelColor` PNG |
|
|
387
|
-
| `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` |
|
|
388
438
|
| `panoptic_segmentation` | 1HW / HW | int64 | COCO-style RGB panoptic PNG → segment IDs |
|
|
389
439
|
| `lidar_point_cloud` | (N, 6) | float64 | `.bin`: `x, y, z, intensity, ring, timestamp` |
|
|
390
440
|
| `sparse_depth` | (N, 6) | float64 | Same points, for sparse-depth workflows |
|
|
@@ -400,7 +450,7 @@ same resolution. Loaders preserve the stored resolution.
|
|
|
400
450
|
| `rgb` | CHW / HWC | float32 | PNG, `[0, 1]` |
|
|
401
451
|
| `depth` | 1HW / HW | float32 | `.npz`, metres |
|
|
402
452
|
| `class_segmentation` | 1HW / HW | int64 | Class IDs from the red channel of an RGBA PNG |
|
|
403
|
-
| `sky_mask` | 1HW / HW | bool |
|
|
453
|
+
| `sky_mask` | 1HW / HW | bool | Head `skyclass` colour, default `[29, 0, 0]` (class ID 29) |
|
|
404
454
|
| `calibration` | dict | — | `{sensor: {"K": (3,3), "T": (4,4), "distortion": (8,)}}` · *hierarchical* |
|
|
405
455
|
| `all_intrinsics` | dict | — | `{sensor: (3, 3)}` · *hierarchical* |
|
|
406
456
|
| `read_intrinsics` | 3×3 | float32 | Defaults to `CS_FRONT` · *hierarchical* |
|
|
@@ -427,7 +477,7 @@ Useful for datasets without a dedicated module.
|
|
|
427
477
|
|---|---|---|---|
|
|
428
478
|
| `rgb` | CHW / HWC | float32 | Image (`.png`, `.jpg`, `.bmp`, `.tif`) or NumPy (`.npy`, `.npz`), `[0, 1]` |
|
|
429
479
|
| `depth` | 1HW / HW | float32 | Image or NumPy, returned as-is; honours `attributes["scale_to_meters_override"]` |
|
|
430
|
-
| `sky_mask` | 1HW / HW | bool | Compares pixels against `
|
|
480
|
+
| `sky_mask` | 1HW / HW | bool | Compares pixels against the `skyclass` colour — required |
|
|
431
481
|
| `read_intrinsics` | 3×3 | float32 | Returns `meta["intrinsics"]`, ignores the path — requires `meta` · *hierarchical* |
|
|
432
482
|
|
|
433
483
|
### Generic NumPy modalities — `generic`
|
|
@@ -444,8 +494,9 @@ assumptions beyond shape and dtype.
|
|
|
444
494
|
| `atmospheric_light` | CHW / HWC | float32 |
|
|
445
495
|
| `scattering_coefficient` | HW | float32 |
|
|
446
496
|
| `semantic_segmentation` | HW | uint8 |
|
|
497
|
+
| `sky_mask` | HW | bool (head `skyclass` label, required) |
|
|
447
498
|
| `instance_segmentation` | HW | uint16 |
|
|
448
499
|
| `intrinsics` | 3×3 | float32 |
|
|
449
500
|
| `sh_coeffs` | (N, C) | float32 |
|
|
450
501
|
|
|
451
|
-
Writers exist for every function in this module
|
|
502
|
+
Writers exist for every function in this module except `sky_mask`.
|
|
@@ -142,8 +142,10 @@ def _resolve_loader(
|
|
|
142
142
|
) -> Callable[..., Any]:
|
|
143
143
|
"""Return the effective loader for a modality.
|
|
144
144
|
|
|
145
|
-
An explicit ``Modality.loader`` always wins;
|
|
146
|
-
|
|
145
|
+
An explicit ``Modality.loader`` always wins; ``Modality.function`` replaces
|
|
146
|
+
the dataset head's function within its loader module. *variant* only
|
|
147
|
+
selects between the built-in GPU and CPU modules during automatic
|
|
148
|
+
resolution.
|
|
147
149
|
"""
|
|
148
150
|
if modality.loader is not None:
|
|
149
151
|
return modality.loader
|
|
@@ -156,7 +158,8 @@ def _resolve_loader(
|
|
|
156
158
|
f"'addons.euler_loading' entry."
|
|
157
159
|
)
|
|
158
160
|
|
|
159
|
-
|
|
161
|
+
required = ("loader",) if modality.function is not None else ("loader", "function")
|
|
162
|
+
for key in required:
|
|
160
163
|
if key not in euler_loading_meta:
|
|
161
164
|
raise ValueError(
|
|
162
165
|
f"Modality {modality_name!r}: no explicit loader provided and "
|
|
@@ -165,7 +168,7 @@ def _resolve_loader(
|
|
|
165
168
|
)
|
|
166
169
|
|
|
167
170
|
module_name: str = euler_loading_meta["loader"]
|
|
168
|
-
func_name: str = euler_loading_meta["function"]
|
|
171
|
+
func_name: str = modality.function or euler_loading_meta["function"]
|
|
169
172
|
|
|
170
173
|
module = resolve_loader_module(module_name, variant=variant)
|
|
171
174
|
|
|
@@ -184,6 +187,24 @@ def _resolve_loader(
|
|
|
184
187
|
return func
|
|
185
188
|
|
|
186
189
|
|
|
190
|
+
def describe_loader(
|
|
191
|
+
loader: Callable[..., Any],
|
|
192
|
+
*,
|
|
193
|
+
modality: Modality,
|
|
194
|
+
index: Mapping[str, Any],
|
|
195
|
+
) -> str:
|
|
196
|
+
"""Name the effective loader for logs, e.g. ``real_drive_sim.sky_mask``."""
|
|
197
|
+
module = getattr(loader, "__module__", None) or ""
|
|
198
|
+
name = getattr(loader, "__qualname__", None) or repr(loader)
|
|
199
|
+
label = f"{module.rsplit('.', 1)[-1]}.{name}" if module else name
|
|
200
|
+
if modality.loader is not None:
|
|
201
|
+
return f"{label} (explicit loader)"
|
|
202
|
+
declared = as_non_empty_str((_get_euler_loading_meta(index) or {}).get("function"))
|
|
203
|
+
if modality.function is not None and declared not in (None, modality.function):
|
|
204
|
+
return f"{label} (the index declares {declared!r})"
|
|
205
|
+
return label
|
|
206
|
+
|
|
207
|
+
|
|
187
208
|
def _writer_name_candidates(read_function_name: str, explicit: Any) -> list[str]:
|
|
188
209
|
names: list[str] = []
|
|
189
210
|
explicit_name = as_non_empty_str(explicit)
|
|
@@ -220,7 +241,9 @@ def _resolve_writer(
|
|
|
220
241
|
return None
|
|
221
242
|
|
|
222
243
|
module_name = as_non_empty_str(euler_loading_meta.get("loader"))
|
|
223
|
-
read_function_name = as_non_empty_str(
|
|
244
|
+
read_function_name = as_non_empty_str(
|
|
245
|
+
modality.function or euler_loading_meta.get("function")
|
|
246
|
+
)
|
|
224
247
|
if module_name is None or read_function_name is None:
|
|
225
248
|
return None
|
|
226
249
|
|
|
@@ -234,9 +257,12 @@ def _resolve_writer(
|
|
|
234
257
|
)
|
|
235
258
|
return None
|
|
236
259
|
|
|
260
|
+
# The head's writer_function pairs with its own function, not an override.
|
|
237
261
|
writer_names = _writer_name_candidates(
|
|
238
262
|
read_function_name,
|
|
239
|
-
|
|
263
|
+
None
|
|
264
|
+
if modality.function is not None
|
|
265
|
+
else euler_loading_meta.get("writer_function"),
|
|
240
266
|
)
|
|
241
267
|
for writer_name in writer_names:
|
|
242
268
|
writer = getattr(module, writer_name, None)
|
|
@@ -148,6 +148,9 @@ def _render_modality(check: ModalityCheck) -> list[str]:
|
|
|
148
148
|
detail = f"FAILED {sample.error}"
|
|
149
149
|
lines.append(f"{_INDENT * 2}{name} -> {detail}")
|
|
150
150
|
|
|
151
|
+
if check.sky:
|
|
152
|
+
lines.append(_field("sky", check.sky))
|
|
153
|
+
|
|
151
154
|
for warning in check.warnings:
|
|
152
155
|
lines.append(_field("warning", warning))
|
|
153
156
|
for error in check.errors:
|
|
@@ -43,6 +43,7 @@ from ._metadata import _build_runlog_entry, _get_ds_crawler_descriptor
|
|
|
43
43
|
from ._resolution import (
|
|
44
44
|
_resolve_loader,
|
|
45
45
|
_resolve_writer,
|
|
46
|
+
describe_loader,
|
|
46
47
|
loader_accepts_attributes,
|
|
47
48
|
resolve_loader_module,
|
|
48
49
|
resolve_writer_module,
|
|
@@ -369,6 +370,12 @@ class Modality:
|
|
|
369
370
|
``metadata["euler_loading"]`` are treated as defaults for
|
|
370
371
|
roles and run logging. They do not select a loader; automatic
|
|
371
372
|
loader resolution uses the dataset-head addon described above.
|
|
373
|
+
function: Optional name of the loader function to use from the module
|
|
374
|
+
that the dataset head's ``addons.euler_loading.loader``
|
|
375
|
+
names, instead of its ``function``. For example
|
|
376
|
+
``"sky_mask"`` reads a ``class_segmentation`` index as
|
|
377
|
+
boolean sky masks. The writer follows the same function.
|
|
378
|
+
Mutually exclusive with *loader*.
|
|
372
379
|
"""
|
|
373
380
|
|
|
374
381
|
path: str
|
|
@@ -385,8 +392,14 @@ class Modality:
|
|
|
385
392
|
cache: bool | None = None
|
|
386
393
|
collapse_single: bool = False
|
|
387
394
|
metadata: dict[str, Any] = field(default_factory=dict)
|
|
395
|
+
function: str | None = None
|
|
388
396
|
|
|
389
397
|
def __post_init__(self) -> None:
|
|
398
|
+
if self.loader is not None and self.function is not None:
|
|
399
|
+
raise ValueError(
|
|
400
|
+
f"Modality {self.path!r} sets both loader and function "
|
|
401
|
+
f"({self.function!r}); pass one of them."
|
|
402
|
+
)
|
|
390
403
|
parsed_path, path_split, path_metadata_scope = parse_modality_path(self.path)
|
|
391
404
|
if path_split is not None:
|
|
392
405
|
if self.split is not None:
|
|
@@ -791,11 +804,14 @@ class MultiModalDataset(_BaseDataset):
|
|
|
791
804
|
}
|
|
792
805
|
self._lookups[name] = lookup
|
|
793
806
|
logger.info(
|
|
794
|
-
"Modality '%s': indexed %d files from %s%s",
|
|
807
|
+
"Modality '%s': indexed %d files from %s%s, loaded with %s",
|
|
795
808
|
name,
|
|
796
809
|
len(lookup),
|
|
797
810
|
modality.path,
|
|
798
811
|
_modality_selector_suffix(modality),
|
|
812
|
+
describe_loader(
|
|
813
|
+
self._resolved_loaders[name], modality=modality, index=index
|
|
814
|
+
),
|
|
799
815
|
)
|
|
800
816
|
|
|
801
817
|
# -- Index hierarchical modalities -----------------------------------
|
|
@@ -825,12 +841,15 @@ class MultiModalDataset(_BaseDataset):
|
|
|
825
841
|
total_files = sum(len(f) for f in files_by_level.values())
|
|
826
842
|
logger.info(
|
|
827
843
|
"Hierarchical modality '%s': indexed %d files across %d "
|
|
828
|
-
"hierarchy levels from %s%s",
|
|
844
|
+
"hierarchy levels from %s%s, loaded with %s",
|
|
829
845
|
name,
|
|
830
846
|
total_files,
|
|
831
847
|
len(files_by_level),
|
|
832
848
|
modality.path,
|
|
833
849
|
_modality_selector_suffix(modality),
|
|
850
|
+
describe_loader(
|
|
851
|
+
self._resolved_loaders[name], modality=modality, index=index
|
|
852
|
+
),
|
|
834
853
|
)
|
|
835
854
|
|
|
836
855
|
# -- Compute common IDs (intersection across regular modalities) -----
|