euler-loading 2.2.1__tar.gz → 2.3.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.1 → euler_loading-2.3.0}/PKG-INFO +1 -1
- {euler_loading-2.2.1 → euler_loading-2.3.0}/README.md +13 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/__init__.py +2 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/dataset.py +3 -3
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/preprocessing.py +106 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/pyproject.toml +1 -1
- {euler_loading-2.2.1 → euler_loading-2.3.0}/tests/test_preprocessing.py +60 -2
- {euler_loading-2.2.1 → euler_loading-2.3.0}/.github/workflows/workflow.yml +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/.gitignore +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/_dataset_contract.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/_ds_crawler_utils.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/_metadata.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/_resolution.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/_writing.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/indexing.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/loaders/__init__.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/loaders/_annotations.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/loaders/_writer_utils.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/loaders/contracts.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/loaders/cpu/__init__.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/loaders/cpu/generic.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/loaders/cpu/generic_dense_depth.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/loaders/cpu/real_drive_sim.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/loaders/cpu/vkitti2.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/loaders/generate/__init__.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/loaders/generate/__main__.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/loaders/generate/loaders.json +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/loaders/generic.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/loaders/gpu/__init__.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/loaders/gpu/generic.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/loaders/gpu/generic_dense_depth.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/loaders/gpu/real_drive_sim.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/loaders/gpu/vkitti2.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/loaders/real_drive_sim.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/euler_loading/loaders/vkitti2.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/example.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/package-lock.json +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/sample_rds.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/tests/__init__.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/tests/conftest.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/tests/example_rds_calib.json +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/tests/test_dataset.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/tests/test_indexing.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/tests/test_loaders.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/tests/test_real_dataset.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/tests/test_writing.py +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.0}/vkitti_cpu_example_output.json +0 -0
- {euler_loading-2.2.1 → euler_loading-2.3.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",
|
|
@@ -471,16 +471,16 @@ class MultiModalDataset(_BaseDataset):
|
|
|
471
471
|
if callable(bind):
|
|
472
472
|
bind(self)
|
|
473
473
|
if self._transforms:
|
|
474
|
-
logger.
|
|
474
|
+
logger.warning("Configured %d sample transform(s):", len(self._transforms))
|
|
475
475
|
for idx, transform in enumerate(self._transforms, start=1):
|
|
476
|
-
logger.
|
|
476
|
+
logger.warning(
|
|
477
477
|
"Transform %d/%d: %s",
|
|
478
478
|
idx,
|
|
479
479
|
len(self._transforms),
|
|
480
480
|
_describe_transform(transform),
|
|
481
481
|
)
|
|
482
482
|
else:
|
|
483
|
-
logger.
|
|
483
|
+
logger.warning("Configured 0 sample transforms.")
|
|
484
484
|
|
|
485
485
|
def get_modality_metadata(self, modality_name: str) -> dict[str, Any]:
|
|
486
486
|
"""Return the metadata dict for a given modality name."""
|
|
@@ -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
|
|
|
@@ -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"])
|
|
@@ -208,7 +265,7 @@ class TestDatasetTransformBinding:
|
|
|
208
265
|
with patch(
|
|
209
266
|
"euler_loading.dataset.index_dataset_from_path",
|
|
210
267
|
return_value=rgb_index,
|
|
211
|
-
), caplog.at_level(logging.
|
|
268
|
+
), caplog.at_level(logging.WARNING, logger="euler_loading.dataset"):
|
|
212
269
|
MultiModalDataset(
|
|
213
270
|
modalities={
|
|
214
271
|
"rgb": Modality(
|
|
@@ -222,6 +279,7 @@ class TestDatasetTransformBinding:
|
|
|
222
279
|
|
|
223
280
|
messages = [record.message for record in caplog.records]
|
|
224
281
|
assert messages.count("Configured 2 sample transform(s):") == 1
|
|
282
|
+
assert all(record.levelno == logging.WARNING for record in caplog.records)
|
|
225
283
|
|
|
226
284
|
joined = "\n".join(messages)
|
|
227
285
|
assert "SamplePreprocessor(" in joined
|
|
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.1 → euler_loading-2.3.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.1 → euler_loading-2.3.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
|
|
File without changes
|