euler-loading 2.2.0__tar.gz → 2.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. {euler_loading-2.2.0 → euler_loading-2.2.2}/PKG-INFO +1 -1
  2. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/dataset.py +37 -0
  3. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/preprocessing.py +63 -0
  4. {euler_loading-2.2.0 → euler_loading-2.2.2}/pyproject.toml +1 -1
  5. {euler_loading-2.2.0 → euler_loading-2.2.2}/tests/test_preprocessing.py +50 -0
  6. {euler_loading-2.2.0 → euler_loading-2.2.2}/.github/workflows/workflow.yml +0 -0
  7. {euler_loading-2.2.0 → euler_loading-2.2.2}/.gitignore +0 -0
  8. {euler_loading-2.2.0 → euler_loading-2.2.2}/README.md +0 -0
  9. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/__init__.py +0 -0
  10. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/_dataset_contract.py +0 -0
  11. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/_ds_crawler_utils.py +0 -0
  12. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/_metadata.py +0 -0
  13. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/_resolution.py +0 -0
  14. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/_writing.py +0 -0
  15. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/indexing.py +0 -0
  16. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/loaders/__init__.py +0 -0
  17. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/loaders/_annotations.py +0 -0
  18. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/loaders/_writer_utils.py +0 -0
  19. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/loaders/contracts.py +0 -0
  20. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/loaders/cpu/__init__.py +0 -0
  21. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/loaders/cpu/generic.py +0 -0
  22. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/loaders/cpu/generic_dense_depth.py +0 -0
  23. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/loaders/cpu/real_drive_sim.py +0 -0
  24. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/loaders/cpu/vkitti2.py +0 -0
  25. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/loaders/generate/__init__.py +0 -0
  26. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/loaders/generate/__main__.py +0 -0
  27. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/loaders/generate/loaders.json +0 -0
  28. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/loaders/generic.py +0 -0
  29. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/loaders/gpu/__init__.py +0 -0
  30. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/loaders/gpu/generic.py +0 -0
  31. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/loaders/gpu/generic_dense_depth.py +0 -0
  32. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/loaders/gpu/real_drive_sim.py +0 -0
  33. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/loaders/gpu/vkitti2.py +0 -0
  34. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/loaders/real_drive_sim.py +0 -0
  35. {euler_loading-2.2.0 → euler_loading-2.2.2}/euler_loading/loaders/vkitti2.py +0 -0
  36. {euler_loading-2.2.0 → euler_loading-2.2.2}/example.py +0 -0
  37. {euler_loading-2.2.0 → euler_loading-2.2.2}/package-lock.json +0 -0
  38. {euler_loading-2.2.0 → euler_loading-2.2.2}/sample_rds.py +0 -0
  39. {euler_loading-2.2.0 → euler_loading-2.2.2}/tests/__init__.py +0 -0
  40. {euler_loading-2.2.0 → euler_loading-2.2.2}/tests/conftest.py +0 -0
  41. {euler_loading-2.2.0 → euler_loading-2.2.2}/tests/example_rds_calib.json +0 -0
  42. {euler_loading-2.2.0 → euler_loading-2.2.2}/tests/test_dataset.py +0 -0
  43. {euler_loading-2.2.0 → euler_loading-2.2.2}/tests/test_indexing.py +0 -0
  44. {euler_loading-2.2.0 → euler_loading-2.2.2}/tests/test_loaders.py +0 -0
  45. {euler_loading-2.2.0 → euler_loading-2.2.2}/tests/test_real_dataset.py +0 -0
  46. {euler_loading-2.2.0 → euler_loading-2.2.2}/tests/test_writing.py +0 -0
  47. {euler_loading-2.2.0 → euler_loading-2.2.2}/vkitti_cpu_example_output.json +0 -0
  48. {euler_loading-2.2.0 → euler_loading-2.2.2}/vkitti_gpu_example_output.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: euler-loading
3
- Version: 2.2.0
3
+ Version: 2.2.2
4
4
  Summary: Multi-modal PyTorch dataloader using ds-crawler indices
5
5
  Requires-Python: >=3.9
6
6
  Requires-Dist: ds-crawler
@@ -59,6 +59,32 @@ __all__ = [
59
59
  ]
60
60
 
61
61
 
62
+ def _callable_name(value: Any) -> str:
63
+ module = getattr(value, "__module__", None)
64
+ qualname = getattr(value, "__qualname__", None)
65
+ if module and qualname:
66
+ return f"{module}.{qualname}"
67
+ name = getattr(value, "__name__", None)
68
+ if module and name:
69
+ return f"{module}.{name}"
70
+ cls = value.__class__
71
+ return f"{cls.__module__}.{cls.__qualname__}"
72
+
73
+
74
+ def _describe_transform(transform: Any) -> str:
75
+ describe = getattr(transform, "describe", None)
76
+ if callable(describe):
77
+ try:
78
+ return str(describe())
79
+ except Exception:
80
+ logger.debug(
81
+ "Failed to describe transform %s; falling back to callable name.",
82
+ _callable_name(transform),
83
+ exc_info=True,
84
+ )
85
+ return _callable_name(transform)
86
+
87
+
62
88
  def _get_index_tree(index_output: Mapping[str, Any]) -> dict[str, Any]:
63
89
  """Return the dataset tree from a ds-crawler output payload."""
64
90
  index_tree = index_output.get("index")
@@ -444,6 +470,17 @@ class MultiModalDataset(_BaseDataset):
444
470
  bind = getattr(transform, "bind_to_dataset", None)
445
471
  if callable(bind):
446
472
  bind(self)
473
+ if self._transforms:
474
+ logger.warning("Configured %d sample transform(s):", len(self._transforms))
475
+ for idx, transform in enumerate(self._transforms, start=1):
476
+ logger.warning(
477
+ "Transform %d/%d: %s",
478
+ idx,
479
+ len(self._transforms),
480
+ _describe_transform(transform),
481
+ )
482
+ else:
483
+ logger.warning("Configured 0 sample transforms.")
447
484
 
448
485
  def get_modality_metadata(self, modality_name: str) -> dict[str, Any]:
449
486
  """Return the metadata dict for a given modality name."""
@@ -186,6 +186,30 @@ class FieldSpec:
186
186
  def is_passthrough(self) -> bool:
187
187
  return self.kind == "passthrough"
188
188
 
189
+ def describe(self, *, include_defaults: bool = True) -> str:
190
+ """Return a concise human-readable description of this field policy."""
191
+ parts = [f"kind={self.kind}"]
192
+ if self.layout is not None:
193
+ parts.append(f"layout={self.layout}")
194
+
195
+ interpolation = self.interpolation
196
+ if include_defaults and interpolation is None:
197
+ interpolation = _DEFAULT_INTERPOLATION[self.kind]
198
+ if interpolation is not None:
199
+ parts.append(f"interpolation={interpolation}")
200
+
201
+ normalize_vectors = self.normalize_vectors
202
+ if include_defaults and normalize_vectors is None:
203
+ normalize_vectors = self.kind == "ray_map"
204
+ if normalize_vectors is not None:
205
+ parts.append(f"normalize_vectors={bool(normalize_vectors)}")
206
+
207
+ if self.kind == "mask" or self.threshold != 0.5:
208
+ parts.append(f"threshold={self.threshold:g}")
209
+ if self.reduce is not None:
210
+ parts.append(f"reduce={self.reduce}")
211
+ return ", ".join(parts)
212
+
189
213
 
190
214
  @dataclass(frozen=True)
191
215
  class Resize:
@@ -202,6 +226,10 @@ class Resize:
202
226
  def from_config(cls, cfg: Any) -> "Resize":
203
227
  return cls(size=_parse_size(cfg, context="Resize"))
204
228
 
229
+ def describe(self) -> str:
230
+ height, width = self.size
231
+ return f"resize({height}x{width})"
232
+
205
233
 
206
234
  @dataclass(frozen=True)
207
235
  class Crop:
@@ -246,6 +274,13 @@ class Crop:
246
274
  )
247
275
  return cls(size=_parse_size(cfg, context="Crop"))
248
276
 
277
+ def describe(self) -> str:
278
+ height, width = self.size
279
+ parts = [f"crop({height}x{width})", f"anchor={self.anchor}"]
280
+ if self.offset is not None:
281
+ parts.append(f"offset={self.offset}")
282
+ return ", ".join(parts)
283
+
249
284
  def resolve_box(self, source_size: tuple[int, int]) -> tuple[int, int, int, int]:
250
285
  source_h, source_w = source_size
251
286
  target_h, target_w = self.size
@@ -687,6 +722,34 @@ class SamplePreprocessor:
687
722
  bind_group(getattr(dataset, "_hierarchical_modalities", {}))
688
723
  self._bound_field_specs = bound_specs
689
724
 
725
+ def describe(self) -> str:
726
+ """Return a concise summary suitable for initialization logging."""
727
+ operations = ", ".join(
728
+ operation.describe() if hasattr(operation, "describe") else repr(operation)
729
+ for operation in self.operations
730
+ ) or "none"
731
+
732
+ active_specs = self._bound_field_specs or self.field_specs
733
+ if active_specs:
734
+ field_parts = [
735
+ f"{name}: {spec.describe()}"
736
+ for name, spec in sorted(active_specs.items())
737
+ ]
738
+ fields_text = "; ".join(field_parts)
739
+ elif self.infer_fields:
740
+ fields_text = "inferred from modality_type/name heuristics"
741
+ else:
742
+ fields_text = "none"
743
+
744
+ reference = self.reference_field or "auto"
745
+ return (
746
+ "SamplePreprocessor("
747
+ f"operations=[{operations}], "
748
+ f"reference_field={reference}, "
749
+ f"infer_fields={self.infer_fields}, "
750
+ f"fields={fields_text})"
751
+ )
752
+
690
753
  def _resolve_field_spec(self, key: str, value: Any) -> FieldSpec | None:
691
754
  if key in self._bound_field_specs:
692
755
  return self._bound_field_specs[key]
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "euler-loading"
3
- version = "2.2.0"
3
+ version = "2.2.2"
4
4
  description = "Multi-modal PyTorch dataloader using ds-crawler indices"
5
5
  requires-python = ">=3.9"
6
6
  dependencies = [
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import logging
5
6
  from unittest.mock import patch
6
7
 
7
8
  import numpy as np
@@ -183,3 +184,52 @@ class TestDatasetTransformBinding:
183
184
  sample = dataset[0]
184
185
  assert sample["rgb"].shape == (3, 2, 3)
185
186
  assert sample["metric_depth"].shape == (1, 2, 3)
187
+
188
+ def test_dataset_logs_transform_behaviour_once_on_init(self, caplog):
189
+ rgb_index = _flat_index("rgb", ["f001"])
190
+
191
+ def rgb_loader(path, meta=None):
192
+ return torch.ones((3, 4, 6), dtype=torch.float32)
193
+
194
+ def identity(sample):
195
+ return sample
196
+
197
+ preprocessor = SamplePreprocessor.from_config(
198
+ {
199
+ "resize": [2, 3],
200
+ "infer_fields": False,
201
+ "fields": {
202
+ "valid_mask": {"kind": "mask"},
203
+ "intrinsics": {"kind": "intrinsics", "reduce": "first"},
204
+ },
205
+ }
206
+ )
207
+
208
+ with patch(
209
+ "euler_loading.dataset.index_dataset_from_path",
210
+ return_value=rgb_index,
211
+ ), caplog.at_level(logging.WARNING, logger="euler_loading.dataset"):
212
+ MultiModalDataset(
213
+ modalities={
214
+ "rgb": Modality(
215
+ "/data/rgb",
216
+ loader=rgb_loader,
217
+ modality_type="rgb",
218
+ ),
219
+ },
220
+ transforms=[preprocessor, identity],
221
+ )
222
+
223
+ messages = [record.message for record in caplog.records]
224
+ assert messages.count("Configured 2 sample transform(s):") == 1
225
+ assert all(record.levelno == logging.WARNING for record in caplog.records)
226
+
227
+ joined = "\n".join(messages)
228
+ assert "SamplePreprocessor(" in joined
229
+ assert "resize(2x3)" in joined
230
+ assert "rgb: kind=image" in joined
231
+ assert "valid_mask: kind=mask" in joined
232
+ assert "interpolation=nearest" in joined
233
+ assert "intrinsics: kind=intrinsics" in joined
234
+ assert "reduce=first" in joined
235
+ assert "identity" in joined
File without changes
File without changes
File without changes