euler-loading 2.2.2__tar.gz → 2.4.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.2.2 → euler_loading-2.4.0}/PKG-INFO +1 -1
- {euler_loading-2.2.2 → euler_loading-2.4.0}/README.md +13 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/__init__.py +2 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/dataset.py +82 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/preprocessing.py +106 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/package-lock.json +1 -1
- {euler_loading-2.2.2 → euler_loading-2.4.0}/pyproject.toml +1 -1
- euler_loading-2.4.0/tests/test_id_schema.py +116 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/tests/test_preprocessing.py +58 -1
- {euler_loading-2.2.2 → euler_loading-2.4.0}/.github/workflows/workflow.yml +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/.gitignore +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/_dataset_contract.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/_ds_crawler_utils.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/_metadata.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/_resolution.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/_writing.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/indexing.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/loaders/__init__.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/loaders/_annotations.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/loaders/_writer_utils.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/loaders/contracts.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/loaders/cpu/__init__.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/loaders/cpu/generic.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/loaders/cpu/generic_dense_depth.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/loaders/cpu/real_drive_sim.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/loaders/cpu/vkitti2.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/loaders/generate/__init__.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/loaders/generate/__main__.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/loaders/generate/loaders.json +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/loaders/generic.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/loaders/gpu/__init__.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/loaders/gpu/generic.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/loaders/gpu/generic_dense_depth.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/loaders/gpu/real_drive_sim.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/loaders/gpu/vkitti2.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/loaders/real_drive_sim.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/loaders/vkitti2.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/example.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/sample_rds.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/tests/__init__.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/tests/conftest.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/tests/example_rds_calib.json +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/tests/test_dataset.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/tests/test_indexing.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/tests/test_loaders.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/tests/test_real_dataset.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/tests/test_writing.py +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/vkitti_cpu_example_output.json +0 -0
- {euler_loading-2.2.2 → euler_loading-2.4.0}/vkitti_gpu_example_output.json +0 -0
|
@@ -218,6 +218,19 @@ def mask_sky_in_depth(sample: dict) -> dict:
|
|
|
218
218
|
return sample
|
|
219
219
|
```
|
|
220
220
|
|
|
221
|
+
For common mask-driven replacements, `MaskedValueOverride` can do the same
|
|
222
|
+
without writing a custom callable:
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
from euler_loading import MaskedValueOverride
|
|
226
|
+
|
|
227
|
+
replace_sky_depth = MaskedValueOverride(
|
|
228
|
+
target_key="depth",
|
|
229
|
+
mask_key="sky_mask",
|
|
230
|
+
value=300.0,
|
|
231
|
+
)
|
|
232
|
+
```
|
|
233
|
+
|
|
221
234
|
### Built-in spatial preprocessing
|
|
222
235
|
|
|
223
236
|
`euler_loading.SamplePreprocessor` applies shared spatial ops such as resize and crop
|
|
@@ -9,6 +9,7 @@ from .loaders.contracts import DenseDepthCodec, DenseDepthLoader, DenseDepthWrit
|
|
|
9
9
|
from .preprocessing import (
|
|
10
10
|
Crop,
|
|
11
11
|
FieldSpec,
|
|
12
|
+
MaskedValueOverride,
|
|
12
13
|
Resize,
|
|
13
14
|
SamplePreprocessor,
|
|
14
15
|
crop_intrinsics,
|
|
@@ -23,6 +24,7 @@ __all__ = [
|
|
|
23
24
|
"FileRecord",
|
|
24
25
|
"Crop",
|
|
25
26
|
"FieldSpec",
|
|
27
|
+
"MaskedValueOverride",
|
|
26
28
|
"Modality",
|
|
27
29
|
"MultiModalDataset",
|
|
28
30
|
"Resize",
|
|
@@ -118,6 +118,36 @@ def _get_index_meta(index_output: Mapping[str, Any]) -> dict[str, Any] | None:
|
|
|
118
118
|
return None
|
|
119
119
|
|
|
120
120
|
|
|
121
|
+
def _extract_id_schema(index_output: Mapping[str, Any]) -> dict[str, Any]:
|
|
122
|
+
"""Pull id/hierarchy separators from a ds-crawler output payload.
|
|
123
|
+
|
|
124
|
+
Supports both modern (``indexing.id.join_char``) and legacy
|
|
125
|
+
(top-level ``id_regex_join_char``) layouts. Missing keys fall back to
|
|
126
|
+
ds-crawler defaults so the returned dict is always usable.
|
|
127
|
+
"""
|
|
128
|
+
indexing = index_output.get("indexing")
|
|
129
|
+
indexing_map = indexing if isinstance(indexing, Mapping) else {}
|
|
130
|
+
id_cfg = indexing_map.get("id")
|
|
131
|
+
id_map = id_cfg if isinstance(id_cfg, Mapping) else {}
|
|
132
|
+
hierarchy_cfg = indexing_map.get("hierarchy")
|
|
133
|
+
hierarchy_map = hierarchy_cfg if isinstance(hierarchy_cfg, Mapping) else {}
|
|
134
|
+
|
|
135
|
+
id_join_char = id_map.get("join_char") or index_output.get("id_regex_join_char") or "+"
|
|
136
|
+
hierarchy_separator = hierarchy_map.get("separator") or "-"
|
|
137
|
+
id_regex = id_map.get("regex") or index_output.get("id_regex")
|
|
138
|
+
hierarchy_regex = hierarchy_map.get("regex") or index_output.get("hierarchy_regex")
|
|
139
|
+
|
|
140
|
+
schema: dict[str, Any] = {
|
|
141
|
+
"hierarchy_separator": hierarchy_separator,
|
|
142
|
+
"id_join_char": id_join_char,
|
|
143
|
+
}
|
|
144
|
+
if id_regex:
|
|
145
|
+
schema["id_regex"] = id_regex
|
|
146
|
+
if hierarchy_regex:
|
|
147
|
+
schema["hierarchy_regex"] = hierarchy_regex
|
|
148
|
+
return schema
|
|
149
|
+
|
|
150
|
+
|
|
121
151
|
#TODO: might want to add slots=True in a 3.10+ only codebase
|
|
122
152
|
@dataclass(frozen=True)
|
|
123
153
|
class Modality:
|
|
@@ -488,6 +518,58 @@ class MultiModalDataset(_BaseDataset):
|
|
|
488
518
|
meta = _get_index_meta(index)
|
|
489
519
|
return meta or {}
|
|
490
520
|
|
|
521
|
+
def describe_id_schema(self) -> dict[str, Any]:
|
|
522
|
+
"""Return the id-construction schema used by this dataset.
|
|
523
|
+
|
|
524
|
+
Downstream consumers that want to split or match ``sample["full_id"]``
|
|
525
|
+
/ ``sample["id"]`` values back to ds-crawler fields need to know the
|
|
526
|
+
separators used to build them. This method exposes that schema so it
|
|
527
|
+
can be embedded in experiment manifests.
|
|
528
|
+
|
|
529
|
+
Schema keys:
|
|
530
|
+
- ``hierarchy_separator``: the character between the named
|
|
531
|
+
capture group *name* and its *value* within a single id part
|
|
532
|
+
(ds-crawler ``indexing.hierarchy.separator``, typically ``"-"``).
|
|
533
|
+
- ``id_join_char``: the character between id parts (ds-crawler
|
|
534
|
+
``indexing.id.join_char`` or legacy ``id_regex_join_char``,
|
|
535
|
+
default ``"+"``).
|
|
536
|
+
- ``full_id_separator``: the character between hierarchy levels
|
|
537
|
+
and the file id in ``sample["full_id"]`` (currently ``"/"``).
|
|
538
|
+
- ``id_regex`` / ``hierarchy_regex``: the raw regexes, when
|
|
539
|
+
recorded in the ds-crawler output.
|
|
540
|
+
- ``modalities``: per-modality overrides populated only when
|
|
541
|
+
modalities disagree on the above.
|
|
542
|
+
|
|
543
|
+
Example::
|
|
544
|
+
|
|
545
|
+
{
|
|
546
|
+
"hierarchy_separator": "-",
|
|
547
|
+
"id_join_char": "+",
|
|
548
|
+
"full_id_separator": "/",
|
|
549
|
+
"id_regex": "...",
|
|
550
|
+
}
|
|
551
|
+
"""
|
|
552
|
+
per_modality: dict[str, dict[str, Any]] = {}
|
|
553
|
+
for name, index in self._index_outputs.items():
|
|
554
|
+
per_modality[name] = _extract_id_schema(index)
|
|
555
|
+
|
|
556
|
+
if not per_modality:
|
|
557
|
+
return {}
|
|
558
|
+
|
|
559
|
+
base = dict(next(iter(per_modality.values())))
|
|
560
|
+
base["full_id_separator"] = "/"
|
|
561
|
+
|
|
562
|
+
overrides: dict[str, dict[str, Any]] = {}
|
|
563
|
+
for mod_name, mod_schema in per_modality.items():
|
|
564
|
+
mod_with_separator = dict(mod_schema)
|
|
565
|
+
mod_with_separator["full_id_separator"] = "/"
|
|
566
|
+
if mod_with_separator != base:
|
|
567
|
+
overrides[mod_name] = mod_with_separator
|
|
568
|
+
|
|
569
|
+
if overrides:
|
|
570
|
+
base["modalities"] = overrides
|
|
571
|
+
return base
|
|
572
|
+
|
|
491
573
|
def get_modality_index(self, modality_name: str) -> dict[str, Any]:
|
|
492
574
|
"""Return the cached ds-crawler index for a modality."""
|
|
493
575
|
if modality_name in self._modalities:
|
|
@@ -17,6 +17,7 @@ except ImportError: # pragma: no cover - exercised in CPU-only environments
|
|
|
17
17
|
|
|
18
18
|
__all__ = [
|
|
19
19
|
"FieldSpec",
|
|
20
|
+
"MaskedValueOverride",
|
|
20
21
|
"Resize",
|
|
21
22
|
"Crop",
|
|
22
23
|
"SamplePreprocessor",
|
|
@@ -310,6 +311,111 @@ class Crop:
|
|
|
310
311
|
return top, left, target_h, target_w
|
|
311
312
|
|
|
312
313
|
|
|
314
|
+
@dataclass(frozen=True)
|
|
315
|
+
class MaskedValueOverride:
|
|
316
|
+
"""Set values in one sample field wherever a boolean mask field is true."""
|
|
317
|
+
|
|
318
|
+
target_key: str
|
|
319
|
+
mask_key: str
|
|
320
|
+
value: Any
|
|
321
|
+
invert_mask: bool = False
|
|
322
|
+
copy: bool = True
|
|
323
|
+
ignore_missing: bool = False
|
|
324
|
+
|
|
325
|
+
@classmethod
|
|
326
|
+
def from_config(cls, cfg: Mapping[str, Any]) -> "MaskedValueOverride":
|
|
327
|
+
if not isinstance(cfg, Mapping):
|
|
328
|
+
raise TypeError(
|
|
329
|
+
"MaskedValueOverride config must be a mapping, "
|
|
330
|
+
f"got {type(cfg).__name__}."
|
|
331
|
+
)
|
|
332
|
+
return cls(
|
|
333
|
+
target_key=str(cfg.get("target_key", cfg.get("target", ""))),
|
|
334
|
+
mask_key=str(cfg.get("mask_key", cfg.get("mask", ""))),
|
|
335
|
+
value=cfg.get("value"),
|
|
336
|
+
invert_mask=bool(cfg.get("invert_mask", False)),
|
|
337
|
+
copy=bool(cfg.get("copy", True)),
|
|
338
|
+
ignore_missing=bool(cfg.get("ignore_missing", False)),
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
def __post_init__(self) -> None:
|
|
342
|
+
if not self.target_key:
|
|
343
|
+
raise ValueError("MaskedValueOverride requires target_key.")
|
|
344
|
+
if not self.mask_key:
|
|
345
|
+
raise ValueError("MaskedValueOverride requires mask_key.")
|
|
346
|
+
|
|
347
|
+
def describe(self) -> str:
|
|
348
|
+
parts = [
|
|
349
|
+
f"masked_value_override(target={self.target_key}",
|
|
350
|
+
f"mask={self.mask_key}",
|
|
351
|
+
f"value={self.value!r}",
|
|
352
|
+
]
|
|
353
|
+
if self.invert_mask:
|
|
354
|
+
parts.append("invert_mask=True")
|
|
355
|
+
if not self.copy:
|
|
356
|
+
parts.append("copy=False")
|
|
357
|
+
if self.ignore_missing:
|
|
358
|
+
parts.append("ignore_missing=True")
|
|
359
|
+
return ", ".join(parts) + ")"
|
|
360
|
+
|
|
361
|
+
def _missing(self, key: str) -> None:
|
|
362
|
+
if self.ignore_missing:
|
|
363
|
+
return
|
|
364
|
+
raise KeyError(f"MaskedValueOverride missing sample key: {key!r}")
|
|
365
|
+
|
|
366
|
+
def __call__(self, sample: dict[str, Any]) -> dict[str, Any]:
|
|
367
|
+
if self.target_key not in sample:
|
|
368
|
+
self._missing(self.target_key)
|
|
369
|
+
return sample
|
|
370
|
+
if self.mask_key not in sample:
|
|
371
|
+
self._missing(self.mask_key)
|
|
372
|
+
return sample
|
|
373
|
+
|
|
374
|
+
target = sample[self.target_key]
|
|
375
|
+
mask = sample[self.mask_key]
|
|
376
|
+
|
|
377
|
+
processed = dict(sample) if self.copy else sample
|
|
378
|
+
|
|
379
|
+
if _is_torch_tensor(target):
|
|
380
|
+
if _is_torch_tensor(mask):
|
|
381
|
+
mask_tensor = mask.to(device=target.device, dtype=torch.bool)
|
|
382
|
+
else:
|
|
383
|
+
mask_tensor = torch.as_tensor(
|
|
384
|
+
mask,
|
|
385
|
+
device=target.device,
|
|
386
|
+
dtype=torch.bool,
|
|
387
|
+
)
|
|
388
|
+
if self.invert_mask:
|
|
389
|
+
mask_tensor = ~mask_tensor
|
|
390
|
+
if tuple(mask_tensor.shape) != tuple(target.shape):
|
|
391
|
+
mask_tensor = torch.broadcast_to(mask_tensor, target.shape)
|
|
392
|
+
value_tensor = torch.as_tensor(
|
|
393
|
+
self.value,
|
|
394
|
+
device=target.device,
|
|
395
|
+
dtype=target.dtype,
|
|
396
|
+
)
|
|
397
|
+
result = target.clone() if self.copy else target
|
|
398
|
+
result[mask_tensor] = value_tensor
|
|
399
|
+
processed[self.target_key] = result
|
|
400
|
+
return processed
|
|
401
|
+
|
|
402
|
+
if isinstance(target, np.ndarray):
|
|
403
|
+
mask_array = np.asarray(mask, dtype=np.bool_)
|
|
404
|
+
if self.invert_mask:
|
|
405
|
+
mask_array = ~mask_array
|
|
406
|
+
if tuple(mask_array.shape) != tuple(target.shape):
|
|
407
|
+
mask_array = np.broadcast_to(mask_array, target.shape)
|
|
408
|
+
result = np.array(target, copy=self.copy)
|
|
409
|
+
result[mask_array] = self.value
|
|
410
|
+
processed[self.target_key] = result
|
|
411
|
+
return processed
|
|
412
|
+
|
|
413
|
+
raise TypeError(
|
|
414
|
+
f"MaskedValueOverride target {self.target_key!r} must be a torch.Tensor "
|
|
415
|
+
f"or numpy.ndarray, got {type(target).__name__}."
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
|
|
313
419
|
def infer_field_spec(name: str, value: Any | None = None) -> FieldSpec | None:
|
|
314
420
|
"""Best-effort field inference for common modality names."""
|
|
315
421
|
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""Tests for MultiModalDataset.describe_id_schema."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
from unittest.mock import patch
|
|
7
|
+
|
|
8
|
+
from euler_loading import Modality, MultiModalDataset
|
|
9
|
+
|
|
10
|
+
from .conftest import dummy_loader
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _flat_index(
|
|
14
|
+
file_ids: list[str],
|
|
15
|
+
*,
|
|
16
|
+
indexing: dict[str, Any] | None = None,
|
|
17
|
+
legacy_join_char: str | None = None,
|
|
18
|
+
) -> dict[str, Any]:
|
|
19
|
+
index: dict[str, Any] = {
|
|
20
|
+
"name": "test-ds",
|
|
21
|
+
"type": "depth",
|
|
22
|
+
"euler_train": {"used_as": "input", "modality_type": "depth"},
|
|
23
|
+
"dataset": {
|
|
24
|
+
"files": [
|
|
25
|
+
{
|
|
26
|
+
"id": fid,
|
|
27
|
+
"path": f"scene/{fid}.png",
|
|
28
|
+
"path_properties": {},
|
|
29
|
+
"basename_properties": {},
|
|
30
|
+
}
|
|
31
|
+
for fid in file_ids
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
if indexing is not None:
|
|
36
|
+
index["indexing"] = indexing
|
|
37
|
+
if legacy_join_char is not None:
|
|
38
|
+
index["id_regex_join_char"] = legacy_join_char
|
|
39
|
+
return index
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class TestDescribeIdSchema:
|
|
43
|
+
def test_modern_indexing_block(self):
|
|
44
|
+
index = _flat_index(
|
|
45
|
+
["f001"],
|
|
46
|
+
indexing={
|
|
47
|
+
"id": {"regex": "^(.+)$", "join_char": "+"},
|
|
48
|
+
"hierarchy": {"separator": "-"},
|
|
49
|
+
},
|
|
50
|
+
)
|
|
51
|
+
with patch(
|
|
52
|
+
"euler_loading.dataset.index_dataset_from_path",
|
|
53
|
+
return_value=index,
|
|
54
|
+
):
|
|
55
|
+
ds = MultiModalDataset(
|
|
56
|
+
modalities={"depth": Modality("/data/depth", loader=dummy_loader)}
|
|
57
|
+
)
|
|
58
|
+
schema = ds.describe_id_schema()
|
|
59
|
+
assert schema["hierarchy_separator"] == "-"
|
|
60
|
+
assert schema["id_join_char"] == "+"
|
|
61
|
+
assert schema["full_id_separator"] == "/"
|
|
62
|
+
assert schema["id_regex"] == "^(.+)$"
|
|
63
|
+
|
|
64
|
+
def test_legacy_top_level_join_char(self):
|
|
65
|
+
index = _flat_index(["f001"], legacy_join_char="|")
|
|
66
|
+
with patch(
|
|
67
|
+
"euler_loading.dataset.index_dataset_from_path",
|
|
68
|
+
return_value=index,
|
|
69
|
+
):
|
|
70
|
+
ds = MultiModalDataset(
|
|
71
|
+
modalities={"depth": Modality("/data/depth", loader=dummy_loader)}
|
|
72
|
+
)
|
|
73
|
+
schema = ds.describe_id_schema()
|
|
74
|
+
assert schema["id_join_char"] == "|"
|
|
75
|
+
assert schema["hierarchy_separator"] == "-"
|
|
76
|
+
|
|
77
|
+
def test_missing_indexing_falls_back_to_defaults(self):
|
|
78
|
+
index = _flat_index(["f001"])
|
|
79
|
+
with patch(
|
|
80
|
+
"euler_loading.dataset.index_dataset_from_path",
|
|
81
|
+
return_value=index,
|
|
82
|
+
):
|
|
83
|
+
ds = MultiModalDataset(
|
|
84
|
+
modalities={"depth": Modality("/data/depth", loader=dummy_loader)}
|
|
85
|
+
)
|
|
86
|
+
schema = ds.describe_id_schema()
|
|
87
|
+
assert schema["hierarchy_separator"] == "-"
|
|
88
|
+
assert schema["id_join_char"] == "+"
|
|
89
|
+
assert schema["full_id_separator"] == "/"
|
|
90
|
+
|
|
91
|
+
def test_divergent_modalities_record_overrides(self):
|
|
92
|
+
rgb_index = _flat_index(
|
|
93
|
+
["f001"],
|
|
94
|
+
indexing={"id": {"join_char": "+"}, "hierarchy": {"separator": "-"}},
|
|
95
|
+
)
|
|
96
|
+
depth_index = _flat_index(
|
|
97
|
+
["f001"],
|
|
98
|
+
indexing={"id": {"join_char": "|"}, "hierarchy": {"separator": "-"}},
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
def mock_index(path: str, **_: Any) -> dict[str, Any]:
|
|
102
|
+
return rgb_index if "rgb" in path else depth_index
|
|
103
|
+
|
|
104
|
+
with patch(
|
|
105
|
+
"euler_loading.dataset.index_dataset_from_path",
|
|
106
|
+
side_effect=mock_index,
|
|
107
|
+
):
|
|
108
|
+
ds = MultiModalDataset(
|
|
109
|
+
modalities={
|
|
110
|
+
"rgb": Modality("/data/rgb", loader=dummy_loader),
|
|
111
|
+
"depth": Modality("/data/depth", loader=dummy_loader),
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
schema = ds.describe_id_schema()
|
|
115
|
+
assert "modalities" in schema
|
|
116
|
+
assert schema["modalities"]["depth"]["id_join_char"] == "|"
|
|
@@ -9,7 +9,7 @@ import numpy as np
|
|
|
9
9
|
import pytest
|
|
10
10
|
import torch
|
|
11
11
|
|
|
12
|
-
from euler_loading import Modality, MultiModalDataset
|
|
12
|
+
from euler_loading import MaskedValueOverride, Modality, MultiModalDataset
|
|
13
13
|
from euler_loading.preprocessing import SamplePreprocessor
|
|
14
14
|
|
|
15
15
|
|
|
@@ -143,6 +143,63 @@ class TestSamplePreprocessorNumpy:
|
|
|
143
143
|
assert np.allclose(processed["intrinsics"], expected_intrinsics)
|
|
144
144
|
|
|
145
145
|
|
|
146
|
+
class TestMaskedValueOverride:
|
|
147
|
+
def test_overrides_torch_target_with_broadcast_mask(self):
|
|
148
|
+
depth = torch.tensor(
|
|
149
|
+
[
|
|
150
|
+
[[1.0, 2.0], [3.0, 4.0]],
|
|
151
|
+
[[5.0, 6.0], [7.0, 8.0]],
|
|
152
|
+
],
|
|
153
|
+
dtype=torch.float32,
|
|
154
|
+
)
|
|
155
|
+
sky_mask = torch.tensor([[[True, False], [False, True]]])
|
|
156
|
+
|
|
157
|
+
transform = MaskedValueOverride(
|
|
158
|
+
target_key="depth",
|
|
159
|
+
mask_key="sky_mask",
|
|
160
|
+
value=0.5,
|
|
161
|
+
)
|
|
162
|
+
processed = transform({"depth": depth, "sky_mask": sky_mask})
|
|
163
|
+
|
|
164
|
+
expected = torch.tensor(
|
|
165
|
+
[
|
|
166
|
+
[[0.5, 2.0], [3.0, 0.5]],
|
|
167
|
+
[[0.5, 6.0], [7.0, 0.5]],
|
|
168
|
+
],
|
|
169
|
+
dtype=torch.float32,
|
|
170
|
+
)
|
|
171
|
+
assert torch.equal(processed["depth"], expected)
|
|
172
|
+
assert torch.equal(
|
|
173
|
+
depth,
|
|
174
|
+
torch.tensor(
|
|
175
|
+
[
|
|
176
|
+
[[1.0, 2.0], [3.0, 4.0]],
|
|
177
|
+
[[5.0, 6.0], [7.0, 8.0]],
|
|
178
|
+
],
|
|
179
|
+
dtype=torch.float32,
|
|
180
|
+
),
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
def test_overrides_numpy_target_with_inverted_mask(self):
|
|
184
|
+
depth = np.array([[1.0, 2.0], [3.0, 4.0]], dtype=np.float32)
|
|
185
|
+
valid_mask = np.array([[True, False], [False, True]], dtype=np.bool_)
|
|
186
|
+
|
|
187
|
+
transform = MaskedValueOverride(
|
|
188
|
+
target_key="depth",
|
|
189
|
+
mask_key="valid_mask",
|
|
190
|
+
value=9.0,
|
|
191
|
+
invert_mask=True,
|
|
192
|
+
)
|
|
193
|
+
processed = transform({"depth": depth, "valid_mask": valid_mask})
|
|
194
|
+
|
|
195
|
+
expected = np.array([[1.0, 9.0], [9.0, 4.0]], dtype=np.float32)
|
|
196
|
+
assert np.array_equal(processed["depth"], expected)
|
|
197
|
+
assert np.array_equal(
|
|
198
|
+
depth,
|
|
199
|
+
np.array([[1.0, 2.0], [3.0, 4.0]], dtype=np.float32),
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
|
|
146
203
|
class TestDatasetTransformBinding:
|
|
147
204
|
def test_dataset_binds_modality_types_into_preprocessor(self):
|
|
148
205
|
rgb_index = _flat_index("rgb", ["f001"])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/loaders/cpu/generic_dense_depth.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{euler_loading-2.2.2 → euler_loading-2.4.0}/euler_loading/loaders/gpu/generic_dense_depth.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|