euler-loading 2.20.0__tar.gz → 2.21.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.
Files changed (59) hide show
  1. {euler_loading-2.20.0 → euler_loading-2.21.0}/PKG-INFO +1 -1
  2. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/cpu/real_drive_sim.py +89 -0
  3. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/generate/loaders.json +56 -0
  4. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/gpu/real_drive_sim.py +96 -1
  5. {euler_loading-2.20.0 → euler_loading-2.21.0}/pyproject.toml +1 -1
  6. {euler_loading-2.20.0 → euler_loading-2.21.0}/tests/test_loaders.py +83 -0
  7. {euler_loading-2.20.0 → euler_loading-2.21.0}/.github/workflows/workflow.yml +0 -0
  8. {euler_loading-2.20.0 → euler_loading-2.21.0}/.gitignore +0 -0
  9. {euler_loading-2.20.0 → euler_loading-2.21.0}/README.md +0 -0
  10. {euler_loading-2.20.0 → euler_loading-2.21.0}/docs/loader-attributes.md +0 -0
  11. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/__init__.py +0 -0
  12. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/_dataset_contract.py +0 -0
  13. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/_ds_crawler_utils.py +0 -0
  14. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/_metadata.py +0 -0
  15. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/_resolution.py +0 -0
  16. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/_writing.py +0 -0
  17. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/dataset.py +0 -0
  18. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/indexing.py +0 -0
  19. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/__init__.py +0 -0
  20. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/_annotations.py +0 -0
  21. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/_princeton_dense.py +0 -0
  22. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/_writer_utils.py +0 -0
  23. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/contracts.py +0 -0
  24. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/cpu/__init__.py +0 -0
  25. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/cpu/generic.py +0 -0
  26. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/cpu/generic_dense_depth.py +0 -0
  27. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/cpu/muses.py +0 -0
  28. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/cpu/princeton_dense.py +0 -0
  29. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/cpu/vkitti2.py +0 -0
  30. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/generate/__init__.py +0 -0
  31. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/generate/__main__.py +0 -0
  32. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/generic.py +0 -0
  33. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/gpu/__init__.py +0 -0
  34. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/gpu/generic.py +0 -0
  35. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/gpu/generic_dense_depth.py +0 -0
  36. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/gpu/muses.py +0 -0
  37. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/gpu/princeton_dense.py +0 -0
  38. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/gpu/vkitti2.py +0 -0
  39. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/muses.py +0 -0
  40. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/princeton_dense.py +0 -0
  41. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/real_drive_sim.py +0 -0
  42. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/loaders/vkitti2.py +0 -0
  43. {euler_loading-2.20.0 → euler_loading-2.21.0}/euler_loading/preprocessing.py +0 -0
  44. {euler_loading-2.20.0 → euler_loading-2.21.0}/example.py +0 -0
  45. {euler_loading-2.20.0 → euler_loading-2.21.0}/gen_loaders.sh +0 -0
  46. {euler_loading-2.20.0 → euler_loading-2.21.0}/package-lock.json +0 -0
  47. {euler_loading-2.20.0 → euler_loading-2.21.0}/sample_rds.py +0 -0
  48. {euler_loading-2.20.0 → euler_loading-2.21.0}/tests/__init__.py +0 -0
  49. {euler_loading-2.20.0 → euler_loading-2.21.0}/tests/conftest.py +0 -0
  50. {euler_loading-2.20.0 → euler_loading-2.21.0}/tests/example_rds_calib.json +0 -0
  51. {euler_loading-2.20.0 → euler_loading-2.21.0}/tests/test_dataset.py +0 -0
  52. {euler_loading-2.20.0 → euler_loading-2.21.0}/tests/test_id_schema.py +0 -0
  53. {euler_loading-2.20.0 → euler_loading-2.21.0}/tests/test_indexing.py +0 -0
  54. {euler_loading-2.20.0 → euler_loading-2.21.0}/tests/test_preprocessing.py +0 -0
  55. {euler_loading-2.20.0 → euler_loading-2.21.0}/tests/test_python_compat.py +0 -0
  56. {euler_loading-2.20.0 → euler_loading-2.21.0}/tests/test_real_dataset.py +0 -0
  57. {euler_loading-2.20.0 → euler_loading-2.21.0}/tests/test_writing.py +0 -0
  58. {euler_loading-2.20.0 → euler_loading-2.21.0}/vkitti_cpu_example_output.json +0 -0
  59. {euler_loading-2.20.0 → euler_loading-2.21.0}/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.20.0
3
+ Version: 2.21.0
4
4
  Summary: Multi-modal PyTorch dataloader using ds-crawler indices
5
5
  Requires-Python: >=3.9
6
6
  Requires-Dist: ds-crawler>=2.3.0
@@ -243,6 +243,95 @@ def read_intrinsics(path: Union[str, BinaryIO], meta: dict[str, Any] | None = No
243
243
  return intrinsics[sensor]
244
244
 
245
245
 
246
+ DEFAULT_CAMERA_SENSOR = "CS_FRONT"
247
+ DEFAULT_LIDAR_SENSOR = "HDL_64E"
248
+
249
+ _SENSOR_ALIASES = {
250
+ "camera": DEFAULT_CAMERA_SENSOR,
251
+ "cam": DEFAULT_CAMERA_SENSOR,
252
+ "rgb": DEFAULT_CAMERA_SENSOR,
253
+ "front": DEFAULT_CAMERA_SENSOR,
254
+ "lidar": DEFAULT_LIDAR_SENSOR,
255
+ "hdl64": DEFAULT_LIDAR_SENSOR,
256
+ "hdl_64": DEFAULT_LIDAR_SENSOR,
257
+ "hdl32": "HDL_32E",
258
+ "hdl_32": "HDL_32E",
259
+ }
260
+
261
+
262
+ def _select_sensor(
263
+ meta: dict[str, Any] | None,
264
+ attributes: dict[str, Any] | None,
265
+ keys: tuple[str, ...],
266
+ default: str,
267
+ ) -> str:
268
+ """Resolve a sensor name from ``attributes`` (highest priority) or ``meta``."""
269
+ for source in (attributes, meta):
270
+ if not isinstance(source, dict):
271
+ continue
272
+ for key in keys:
273
+ value = source.get(key)
274
+ if isinstance(value, str) and value:
275
+ return _SENSOR_ALIASES.get(value.lower(), value)
276
+ return default
277
+
278
+
279
+ @modality_meta(
280
+ modality_type="camera_extrinsics",
281
+ dtype="float32",
282
+ hierarchical=True,
283
+ shape="4x4",
284
+ file_formats=[".json"],
285
+ meta={
286
+ "dataset": "RealDriveSim",
287
+ "default_source_frame": DEFAULT_LIDAR_SENSOR,
288
+ "default_target_frame": DEFAULT_CAMERA_SENSOR,
289
+ "transform_direction": "source_frame_to_target_frame",
290
+ },
291
+ )
292
+ def read_extrinsics(path: Union[str, BinaryIO], meta: dict[str, Any] | None = None, *, attributes: dict[str, Any] | None = None) -> np.ndarray:
293
+ """Load a directed ``source -> target`` sensor transform.
294
+
295
+ Real Drive Sim stores one pose per sensor, each mapping that sensor's
296
+ coordinates into the shared vehicle frame. There is therefore no stored
297
+ sensor-to-sensor matrix; it is composed here::
298
+
299
+ X_target = inv(T_target) @ T_source @ X_source
300
+
301
+ The default is ``HDL_64E -> CS_FRONT``, suitable for projecting Real Drive
302
+ Sim lidar/sparse-depth points into the front camera. Override it with
303
+ ``meta`` or per-file ``attributes`` using ``source_frame`` /
304
+ ``source_sensor`` and ``target_frame`` / ``target_sensor``.
305
+
306
+ Returns a ``(4, 4)`` float32 homogeneous matrix, row-major.
307
+ """
308
+ sensors = calibration(path)
309
+
310
+ source = _select_sensor(
311
+ meta,
312
+ attributes,
313
+ ("source_frame", "source_sensor", "from_frame", "from_sensor", "source"),
314
+ DEFAULT_LIDAR_SENSOR,
315
+ )
316
+ target = _select_sensor(
317
+ meta,
318
+ attributes,
319
+ ("target_frame", "target_sensor", "to_frame", "to_sensor", "target", "camera"),
320
+ DEFAULT_CAMERA_SENSOR,
321
+ )
322
+
323
+ for role, sensor in (("source", source), ("target", target)):
324
+ if sensor not in sensors:
325
+ raise KeyError(
326
+ f"Real Drive Sim calibration has no {role} sensor {sensor!r}. "
327
+ f"Available sensors: {sorted(sensors)}"
328
+ )
329
+
330
+ T_source = np.asarray(sensors[source]["T"], dtype=np.float64)
331
+ T_target = np.asarray(sensors[target]["T"], dtype=np.float64)
332
+ return (np.linalg.inv(T_target) @ T_source).astype(np.float32)
333
+
334
+
246
335
  # ---------------------------------------------------------------------------
247
336
  # Writers
248
337
  # ---------------------------------------------------------------------------
@@ -20,6 +20,23 @@
20
20
  "shape": "CHW"
21
21
  }
22
22
  },
23
+ {
24
+ "type": "instance_segmentation",
25
+ "function": "instance_segmentation",
26
+ "hierarchical": false,
27
+ "dtype": "uint16",
28
+ "access": "key",
29
+ "file_formats": [
30
+ ".npy",
31
+ ".npz"
32
+ ],
33
+ "cpu": {
34
+ "shape": "HW"
35
+ },
36
+ "gpu": {
37
+ "shape": "HW"
38
+ }
39
+ },
23
40
  {
24
41
  "type": "intrinsics",
25
42
  "function": "intrinsics",
@@ -105,6 +122,23 @@
105
122
  "shape": "HW"
106
123
  }
107
124
  },
125
+ {
126
+ "type": "semantic_segmentation",
127
+ "function": "semantic_segmentation",
128
+ "hierarchical": false,
129
+ "dtype": "uint8",
130
+ "access": "key",
131
+ "file_formats": [
132
+ ".npy",
133
+ ".npz"
134
+ ],
135
+ "cpu": {
136
+ "shape": "HW"
137
+ },
138
+ "gpu": {
139
+ "shape": "HW"
140
+ }
141
+ },
108
142
  {
109
143
  "type": "sh_coeffs",
110
144
  "function": "sh_coeffs",
@@ -680,6 +714,28 @@
680
714
  "shape": "1HW"
681
715
  }
682
716
  },
717
+ {
718
+ "type": "camera_extrinsics",
719
+ "function": "read_extrinsics",
720
+ "hierarchical": true,
721
+ "dtype": "float32",
722
+ "access": "key",
723
+ "file_formats": [
724
+ ".json"
725
+ ],
726
+ "meta": {
727
+ "dataset": "RealDriveSim",
728
+ "default_source_frame": "HDL_64E",
729
+ "default_target_frame": "CS_FRONT",
730
+ "transform_direction": "source_frame_to_target_frame"
731
+ },
732
+ "cpu": {
733
+ "shape": "4x4"
734
+ },
735
+ "gpu": {
736
+ "shape": "4x4"
737
+ }
738
+ },
683
739
  {
684
740
  "type": "intrinsics",
685
741
  "function": "read_intrinsics",
@@ -230,8 +230,103 @@ def all_intrinsics(path: Union[str, BinaryIO], meta: dict[str, Any] | None = Non
230
230
  )
231
231
  def read_intrinsics(path: Union[str, BinaryIO], meta: dict[str, Any] | None = None, *, attributes: dict[str, Any] | None = None) -> torch.Tensor:
232
232
  """Load the intrinsics for a specific sensor from a Real Drive Sim calibration JSON."""
233
+ sensor = (attributes or {}).get("sensor") or (meta or {}).get("sensor") or "CS_FRONT"
233
234
  all_intrinsics_data = all_intrinsics(path)
234
- return all_intrinsics_data["CS_FRONT"]
235
+ if sensor not in all_intrinsics_data:
236
+ raise KeyError(
237
+ f"Real Drive Sim calibration has no intrinsics for sensor {sensor!r}. "
238
+ f"Available sensors: {sorted(all_intrinsics_data)}"
239
+ )
240
+ return all_intrinsics_data[sensor]
241
+
242
+
243
+ DEFAULT_CAMERA_SENSOR = "CS_FRONT"
244
+ DEFAULT_LIDAR_SENSOR = "HDL_64E"
245
+
246
+ _SENSOR_ALIASES = {
247
+ "camera": DEFAULT_CAMERA_SENSOR,
248
+ "cam": DEFAULT_CAMERA_SENSOR,
249
+ "rgb": DEFAULT_CAMERA_SENSOR,
250
+ "front": DEFAULT_CAMERA_SENSOR,
251
+ "lidar": DEFAULT_LIDAR_SENSOR,
252
+ "hdl64": DEFAULT_LIDAR_SENSOR,
253
+ "hdl_64": DEFAULT_LIDAR_SENSOR,
254
+ "hdl32": "HDL_32E",
255
+ "hdl_32": "HDL_32E",
256
+ }
257
+
258
+
259
+ def _select_sensor(
260
+ meta: dict[str, Any] | None,
261
+ attributes: dict[str, Any] | None,
262
+ keys: tuple[str, ...],
263
+ default: str,
264
+ ) -> str:
265
+ """Resolve a sensor name from ``attributes`` (highest priority) or ``meta``."""
266
+ for source in (attributes, meta):
267
+ if not isinstance(source, dict):
268
+ continue
269
+ for key in keys:
270
+ value = source.get(key)
271
+ if isinstance(value, str) and value:
272
+ return _SENSOR_ALIASES.get(value.lower(), value)
273
+ return default
274
+
275
+
276
+ @modality_meta(
277
+ modality_type="camera_extrinsics",
278
+ dtype="float32",
279
+ hierarchical=True,
280
+ shape="4x4",
281
+ file_formats=[".json"],
282
+ meta={
283
+ "dataset": "RealDriveSim",
284
+ "default_source_frame": DEFAULT_LIDAR_SENSOR,
285
+ "default_target_frame": DEFAULT_CAMERA_SENSOR,
286
+ "transform_direction": "source_frame_to_target_frame",
287
+ },
288
+ )
289
+ def read_extrinsics(path: Union[str, BinaryIO], meta: dict[str, Any] | None = None, *, attributes: dict[str, Any] | None = None) -> torch.Tensor:
290
+ """Load a directed ``source -> target`` sensor transform.
291
+
292
+ Real Drive Sim stores one pose per sensor, each mapping that sensor's
293
+ coordinates into the shared vehicle frame. There is therefore no stored
294
+ sensor-to-sensor matrix; it is composed here::
295
+
296
+ X_target = inv(T_target) @ T_source @ X_source
297
+
298
+ The default is ``HDL_64E -> CS_FRONT``, suitable for projecting Real Drive
299
+ Sim lidar/sparse-depth points into the front camera. Override it with
300
+ ``meta`` or per-file ``attributes`` using ``source_frame`` /
301
+ ``source_sensor`` and ``target_frame`` / ``target_sensor``.
302
+
303
+ Returns a ``(4, 4)`` float32 homogeneous tensor, row-major.
304
+ """
305
+ sensors = calibration(path)
306
+
307
+ source = _select_sensor(
308
+ meta,
309
+ attributes,
310
+ ("source_frame", "source_sensor", "from_frame", "from_sensor", "source"),
311
+ DEFAULT_LIDAR_SENSOR,
312
+ )
313
+ target = _select_sensor(
314
+ meta,
315
+ attributes,
316
+ ("target_frame", "target_sensor", "to_frame", "to_sensor", "target", "camera"),
317
+ DEFAULT_CAMERA_SENSOR,
318
+ )
319
+
320
+ for role, sensor in (("source", source), ("target", target)):
321
+ if sensor not in sensors:
322
+ raise KeyError(
323
+ f"Real Drive Sim calibration has no {role} sensor {sensor!r}. "
324
+ f"Available sensors: {sorted(sensors)}"
325
+ )
326
+
327
+ T_source = sensors[source]["T"].to(torch.float64)
328
+ T_target = sensors[target]["T"].to(torch.float64)
329
+ return (torch.linalg.inv(T_target) @ T_source).to(torch.float32).contiguous()
235
330
 
236
331
 
237
332
  # ---------------------------------------------------------------------------
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "euler-loading"
3
- version = "2.20.0"
3
+ version = "2.21.0"
4
4
  description = "Multi-modal PyTorch dataloader using ds-crawler indices"
5
5
  requires-python = ">=3.9"
6
6
  dependencies = [
@@ -1111,6 +1111,89 @@ class TestRDSCPUCalibration:
1111
1111
  assert K[0, 0] == 0.0
1112
1112
 
1113
1113
 
1114
+ class TestRDSReadExtrinsics:
1115
+ """Real Drive Sim composes sensor-to-sensor transforms from per-sensor poses.
1116
+
1117
+ The dataset stores one pose per sensor in a shared vehicle frame, so the
1118
+ directed transform is derived: ``X_target = inv(T_target) @ T_source @ X_source``.
1119
+ """
1120
+
1121
+ def test_defaults_to_lidar_to_front_camera(self):
1122
+ T = cpu_rds.read_extrinsics(_RDS_CALIB_PATH)
1123
+ assert T.shape == (4, 4)
1124
+ assert T.dtype == np.float32
1125
+ assert np.array_equal(T[3], np.array([0.0, 0.0, 0.0, 1.0], dtype=np.float32))
1126
+
1127
+ def test_matches_explicit_composition(self):
1128
+ sensors = cpu_rds.calibration(_RDS_CALIB_PATH)
1129
+ expected = np.linalg.inv(
1130
+ sensors["CS_FRONT"]["T"].astype(np.float64)
1131
+ ) @ sensors["HDL_64E"]["T"].astype(np.float64)
1132
+ result = cpu_rds.read_extrinsics(_RDS_CALIB_PATH)
1133
+ assert np.allclose(result, expected, atol=1e-6)
1134
+
1135
+ def test_same_source_and_target_is_identity(self):
1136
+ T = cpu_rds.read_extrinsics(
1137
+ _RDS_CALIB_PATH,
1138
+ {"source_frame": "CS_FRONT", "target_frame": "CS_FRONT"},
1139
+ )
1140
+ assert np.allclose(T, np.eye(4), atol=1e-6)
1141
+
1142
+ def test_reversed_direction_is_the_inverse(self):
1143
+ forward = cpu_rds.read_extrinsics(
1144
+ _RDS_CALIB_PATH,
1145
+ {"source_frame": "HDL_64E", "target_frame": "CS_FRONT"},
1146
+ )
1147
+ backward = cpu_rds.read_extrinsics(
1148
+ _RDS_CALIB_PATH,
1149
+ {"source_frame": "CS_FRONT", "target_frame": "HDL_64E"},
1150
+ )
1151
+ assert np.allclose(forward @ backward, np.eye(4), atol=1e-6)
1152
+
1153
+ def test_rotation_block_stays_orthonormal(self):
1154
+ R = cpu_rds.read_extrinsics(_RDS_CALIB_PATH)[:3, :3]
1155
+ assert np.allclose(R @ R.T, np.eye(3), atol=1e-6)
1156
+ assert np.isclose(np.linalg.det(R), 1.0, atol=1e-6)
1157
+
1158
+ def test_sensor_aliases_resolve_to_canonical_names(self):
1159
+ aliased = cpu_rds.read_extrinsics(
1160
+ _RDS_CALIB_PATH, {"source_frame": "lidar", "target_frame": "camera"}
1161
+ )
1162
+ canonical = cpu_rds.read_extrinsics(
1163
+ _RDS_CALIB_PATH,
1164
+ {"source_frame": "HDL_64E", "target_frame": "CS_FRONT"},
1165
+ )
1166
+ assert np.array_equal(aliased, canonical)
1167
+
1168
+ def test_attributes_take_priority_over_meta(self):
1169
+ result = cpu_rds.read_extrinsics(
1170
+ _RDS_CALIB_PATH,
1171
+ {"source_frame": "HDL_64E"},
1172
+ attributes={"source_frame": "HDL_32E"},
1173
+ )
1174
+ expected = cpu_rds.read_extrinsics(_RDS_CALIB_PATH, {"source_frame": "HDL_32E"})
1175
+ assert np.array_equal(result, expected)
1176
+
1177
+ def test_unknown_sensor_names_the_available_ones(self):
1178
+ with pytest.raises(KeyError) as excinfo:
1179
+ cpu_rds.read_extrinsics(_RDS_CALIB_PATH, {"target_frame": "NOT_A_SENSOR"})
1180
+ message = str(excinfo.value)
1181
+ assert "NOT_A_SENSOR" in message
1182
+ assert "CS_FRONT" in message
1183
+
1184
+ def test_gpu_matches_cpu(self):
1185
+ for kwargs in (
1186
+ None,
1187
+ {"source_frame": "HDL_32E"},
1188
+ {"source_frame": "CS_FRONT", "target_frame": "HDL_64E"},
1189
+ ):
1190
+ cpu_result = cpu_rds.read_extrinsics(_RDS_CALIB_PATH, kwargs)
1191
+ gpu_result = gpu_rds.read_extrinsics(_RDS_CALIB_PATH, kwargs)
1192
+ assert gpu_result.shape == (4, 4)
1193
+ assert gpu_result.dtype == torch.float32
1194
+ assert np.allclose(cpu_result, gpu_result.numpy(), atol=1e-6)
1195
+
1196
+
1114
1197
  # ---------------------------------------------------------------------------
1115
1198
  # Writer round-trip tests
1116
1199
  # ---------------------------------------------------------------------------
File without changes