euler-loading 2.19.1__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.
- {euler_loading-2.19.1 → euler_loading-2.21.0}/PKG-INFO +1 -1
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/cpu/princeton_dense.py +19 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/cpu/real_drive_sim.py +89 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/generate/loaders.json +56 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/gpu/princeton_dense.py +19 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/gpu/real_drive_sim.py +96 -1
- {euler_loading-2.19.1 → euler_loading-2.21.0}/pyproject.toml +1 -1
- {euler_loading-2.19.1 → euler_loading-2.21.0}/tests/test_loaders.py +97 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/tests/test_writing.py +50 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/.github/workflows/workflow.yml +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/.gitignore +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/README.md +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/docs/loader-attributes.md +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/__init__.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/_dataset_contract.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/_ds_crawler_utils.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/_metadata.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/_resolution.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/_writing.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/dataset.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/indexing.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/__init__.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/_annotations.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/_princeton_dense.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/_writer_utils.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/contracts.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/cpu/__init__.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/cpu/generic.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/cpu/generic_dense_depth.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/cpu/muses.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/cpu/vkitti2.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/generate/__init__.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/generate/__main__.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/generic.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/gpu/__init__.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/gpu/generic.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/gpu/generic_dense_depth.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/gpu/muses.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/gpu/vkitti2.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/muses.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/princeton_dense.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/real_drive_sim.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/loaders/vkitti2.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/euler_loading/preprocessing.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/example.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/gen_loaders.sh +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/package-lock.json +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/sample_rds.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/tests/__init__.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/tests/conftest.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/tests/example_rds_calib.json +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/tests/test_dataset.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/tests/test_id_schema.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/tests/test_indexing.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/tests/test_preprocessing.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/tests/test_python_compat.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/tests/test_real_dataset.py +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/vkitti_cpu_example_output.json +0 -0
- {euler_loading-2.19.1 → euler_loading-2.21.0}/vkitti_gpu_example_output.json +0 -0
|
@@ -24,6 +24,13 @@ import numpy as np
|
|
|
24
24
|
from PIL import Image
|
|
25
25
|
|
|
26
26
|
from euler_loading.loaders._annotations import modality_meta
|
|
27
|
+
from euler_loading.loaders._writer_utils import (
|
|
28
|
+
ensure_parent,
|
|
29
|
+
mark_stream_supported,
|
|
30
|
+
save_image,
|
|
31
|
+
to_hwc_rgb,
|
|
32
|
+
to_uint8,
|
|
33
|
+
)
|
|
27
34
|
from euler_loading.loaders._princeton_dense import DEFAULT_CAMERA_FRAME
|
|
28
35
|
from euler_loading.loaders._princeton_dense import DEFAULT_LIDAR_FRAME
|
|
29
36
|
from euler_loading.loaders._princeton_dense import LIDAR_COLUMNS
|
|
@@ -73,6 +80,18 @@ def rgb(
|
|
|
73
80
|
return np.array(Image.open(path).convert("RGB"), dtype=np.float32) / 255.0
|
|
74
81
|
|
|
75
82
|
|
|
83
|
+
@mark_stream_supported
|
|
84
|
+
def write_rgb(
|
|
85
|
+
path: Union[str, BinaryIO],
|
|
86
|
+
value: Any,
|
|
87
|
+
meta: dict[str, Any] | None = None,
|
|
88
|
+
) -> None:
|
|
89
|
+
"""Write an RGB array/tensor as an 8-bit PNG."""
|
|
90
|
+
ensure_parent(path)
|
|
91
|
+
arr = to_uint8(to_hwc_rgb(value, name="rgb"), scale_unit_range=True)
|
|
92
|
+
save_image(path, Image.fromarray(arr, mode="RGB"), format="PNG")
|
|
93
|
+
|
|
94
|
+
|
|
76
95
|
@modality_meta(
|
|
77
96
|
modality_type="sparse_depth",
|
|
78
97
|
dtype="float32",
|
|
@@ -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",
|
|
@@ -25,6 +25,13 @@ from PIL import Image
|
|
|
25
25
|
import torch
|
|
26
26
|
|
|
27
27
|
from euler_loading.loaders._annotations import modality_meta
|
|
28
|
+
from euler_loading.loaders._writer_utils import (
|
|
29
|
+
ensure_parent,
|
|
30
|
+
mark_stream_supported,
|
|
31
|
+
save_image,
|
|
32
|
+
to_hwc_rgb,
|
|
33
|
+
to_uint8,
|
|
34
|
+
)
|
|
28
35
|
from euler_loading.loaders._princeton_dense import DEFAULT_CAMERA_FRAME
|
|
29
36
|
from euler_loading.loaders._princeton_dense import DEFAULT_LIDAR_FRAME
|
|
30
37
|
from euler_loading.loaders._princeton_dense import LIDAR_COLUMNS
|
|
@@ -76,6 +83,18 @@ def rgb(
|
|
|
76
83
|
return torch.from_numpy(arr).permute(2, 0, 1).contiguous()
|
|
77
84
|
|
|
78
85
|
|
|
86
|
+
@mark_stream_supported
|
|
87
|
+
def write_rgb(
|
|
88
|
+
path: Union[str, BinaryIO],
|
|
89
|
+
value: Any,
|
|
90
|
+
meta: dict[str, Any] | None = None,
|
|
91
|
+
) -> None:
|
|
92
|
+
"""Write an RGB tensor/array as an 8-bit PNG."""
|
|
93
|
+
ensure_parent(path)
|
|
94
|
+
arr = to_uint8(to_hwc_rgb(value, name="rgb"), scale_unit_range=True)
|
|
95
|
+
save_image(path, Image.fromarray(arr, mode="RGB"), format="PNG")
|
|
96
|
+
|
|
97
|
+
|
|
79
98
|
@modality_meta(
|
|
80
99
|
modality_type="sparse_depth",
|
|
81
100
|
dtype="float32",
|
|
@@ -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
|
-
|
|
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
|
# ---------------------------------------------------------------------------
|
|
@@ -165,6 +165,8 @@ PRINCETON_DENSE_LOADER_NAMES = [
|
|
|
165
165
|
"read_extrinsics",
|
|
166
166
|
]
|
|
167
167
|
|
|
168
|
+
PRINCETON_DENSE_WRITER_NAMES = ["write_rgb"]
|
|
169
|
+
|
|
168
170
|
|
|
169
171
|
@pytest.fixture()
|
|
170
172
|
def princeton_dense_rgb_path(tmp_path):
|
|
@@ -261,6 +263,18 @@ class TestPrincetonDenseModuleContents:
|
|
|
261
263
|
def test_cpu_has_callable(self, name):
|
|
262
264
|
assert callable(getattr(cpu_princeton_dense, name))
|
|
263
265
|
|
|
266
|
+
@pytest.mark.parametrize("name", PRINCETON_DENSE_WRITER_NAMES)
|
|
267
|
+
def test_top_level_has_writer(self, name):
|
|
268
|
+
assert callable(getattr(princeton_dense_top, name))
|
|
269
|
+
|
|
270
|
+
@pytest.mark.parametrize("name", PRINCETON_DENSE_WRITER_NAMES)
|
|
271
|
+
def test_gpu_has_writer(self, name):
|
|
272
|
+
assert callable(getattr(gpu_princeton_dense, name))
|
|
273
|
+
|
|
274
|
+
@pytest.mark.parametrize("name", PRINCETON_DENSE_WRITER_NAMES)
|
|
275
|
+
def test_cpu_has_writer(self, name):
|
|
276
|
+
assert callable(getattr(cpu_princeton_dense, name))
|
|
277
|
+
|
|
264
278
|
def test_auto_resolution_uses_gpu_module(self):
|
|
265
279
|
module = resolve_loader_module("princeton_dense")
|
|
266
280
|
assert module.__name__ == "euler_loading.loaders.gpu.princeton_dense"
|
|
@@ -1097,6 +1111,89 @@ class TestRDSCPUCalibration:
|
|
|
1097
1111
|
assert K[0, 0] == 0.0
|
|
1098
1112
|
|
|
1099
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
|
+
|
|
1100
1197
|
# ---------------------------------------------------------------------------
|
|
1101
1198
|
# Writer round-trip tests
|
|
1102
1199
|
# ---------------------------------------------------------------------------
|
|
@@ -14,6 +14,7 @@ from PIL import Image
|
|
|
14
14
|
|
|
15
15
|
from euler_loading import Modality, MultiModalDataset, resolve_writer_module
|
|
16
16
|
from euler_loading.loaders.gpu import vkitti2 as gpu_vkitti2
|
|
17
|
+
from euler_loading.loaders.gpu import princeton_dense as gpu_princeton_dense
|
|
17
18
|
|
|
18
19
|
from .conftest import dummy_loader
|
|
19
20
|
|
|
@@ -92,6 +93,55 @@ class TestWriterResolution:
|
|
|
92
93
|
|
|
93
94
|
assert ds.get_writer("intrinsics") is gpu_vkitti2.write_intrinsics
|
|
94
95
|
|
|
96
|
+
def test_auto_resolves_princeton_dense_rgb_writer(self):
|
|
97
|
+
index = _flat_index(
|
|
98
|
+
"png",
|
|
99
|
+
["f001"],
|
|
100
|
+
euler_loading={"loader": "princeton_dense", "function": "rgb"},
|
|
101
|
+
)
|
|
102
|
+
with patch(
|
|
103
|
+
"euler_loading.dataset.index_dataset_from_path",
|
|
104
|
+
return_value=index,
|
|
105
|
+
):
|
|
106
|
+
ds = MultiModalDataset(modalities={"rgb": Modality("/data/rgb")})
|
|
107
|
+
|
|
108
|
+
assert ds.get_writer("rgb") is gpu_princeton_dense.write_rgb
|
|
109
|
+
|
|
110
|
+
@pytest.mark.parametrize(
|
|
111
|
+
"writer,chw",
|
|
112
|
+
[
|
|
113
|
+
(gpu_princeton_dense.write_rgb, True),
|
|
114
|
+
pytest.param(None, False, id="cpu"),
|
|
115
|
+
],
|
|
116
|
+
)
|
|
117
|
+
def test_princeton_dense_rgb_writer_round_trip(self, tmp_path, writer, chw):
|
|
118
|
+
if writer is None:
|
|
119
|
+
from euler_loading.loaders.cpu import princeton_dense as cpu_princeton_dense
|
|
120
|
+
|
|
121
|
+
writer = cpu_princeton_dense.write_rgb
|
|
122
|
+
expected = np.array(
|
|
123
|
+
[[[0.0, 0.5, 1.0], [1.0, 0.25, 0.0]]],
|
|
124
|
+
dtype=np.float32,
|
|
125
|
+
)
|
|
126
|
+
value = np.transpose(expected, (2, 0, 1)) if chw else expected
|
|
127
|
+
path = tmp_path / ("gpu.png" if chw else "cpu.png")
|
|
128
|
+
|
|
129
|
+
writer(path, value)
|
|
130
|
+
|
|
131
|
+
actual = np.asarray(Image.open(path).convert("RGB"), dtype=np.float32) / 255.0
|
|
132
|
+
np.testing.assert_allclose(actual, expected, atol=1.0 / 255.0)
|
|
133
|
+
|
|
134
|
+
def test_princeton_dense_rgb_writer_supports_binary_stream(self):
|
|
135
|
+
stream = io.BytesIO()
|
|
136
|
+
gpu_princeton_dense.write_rgb(
|
|
137
|
+
stream,
|
|
138
|
+
np.ones((3, 2, 2), dtype=np.float32),
|
|
139
|
+
)
|
|
140
|
+
stream.seek(0)
|
|
141
|
+
image = Image.open(stream)
|
|
142
|
+
assert image.format == "PNG"
|
|
143
|
+
assert image.size == (2, 2)
|
|
144
|
+
|
|
95
145
|
|
|
96
146
|
class TestWriteSample:
|
|
97
147
|
def test_write_sample_preserves_relative_path(self, tmp_path):
|
|
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
|
|
File without changes
|
{euler_loading-2.19.1 → euler_loading-2.21.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
|
{euler_loading-2.19.1 → euler_loading-2.21.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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|