Skip to content

Commit 988e4da

Browse files
committed
feat(nuscenes): add nuScenes to NCore V4 converter
Implements a converter from nuScenes dataset format to NCore V4 component-based format, following the KITTI converter pattern. Features: - Supports all nuScenes versions (v1.0-mini, v1.0-trainval, v1.0-test) - 6 cameras with intrinsics, extrinsics, and frame data - LIDAR_TOP with unstructured point clouds (direction + distance) - Per-point timestamps estimated from azimuth (HDL-32E, ~50ms scan) - 3D cuboid annotations in world frame from keyframe sample_annotations - Scene selection via --scene-token or --scene-name - Outputs itar or directory store formats Sensor assumptions documented: - Cameras: global shutter, undistorted (zero distortion coeffs) - Lidar: Velodyne HDL-32E at 20Hz, unstructured (model_element=None) - Cuboids: world frame, keyframes only Tested with v1.0-mini (22 tests pass, both itar and directory formats). Closes: #123
1 parent c1e21ea commit 988e4da

14 files changed

Lines changed: 3284 additions & 198 deletions

File tree

MODULE.bazel.lock

Lines changed: 776 additions & 134 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

deps/pip/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ pip_compile(
3737
":requirements_colmap.in",
3838
":requirements_docs.in",
3939
":requirements_ncore.in",
40+
":requirements_nuscenes.in",
4041
":requirements_pai.in",
4142
":requirements_tests.in",
4243
":requirements_tools.in",

deps/pip/requirements_3_11.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
-r requirements_tools.in
2121
-r requirements_waymo.in
2222
-r requirements_colmap.in
23+
-r requirements_nuscenes.in
2324
-r requirements_pai.in
2425

2526
# Public API restrictions for 3.11

deps/pip/requirements_3_11.txt

Lines changed: 180 additions & 62 deletions
Large diffs are not rendered by default.

deps/pip/requirements_nuscenes.in

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
# nuScenes converter dependencies
17+
nuscenes-devkit
18+
# pyquaternion is a transitive dependency of nuscenes-devkit that we also use directly
19+
# for quaternion-to-rotation-matrix conversion and SLERP interpolation.
20+
pyquaternion

docs/conversions/index.rst

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@ Data Conversions
55
================
66

77
NCore provides conversion tools for importing 3rd-party dataset formats into
8-
the NCore V4 component-based format. Supported formats include KITTI, Waymo,
9-
COLMAP (including ScanNet++), and PAI.
8+
the NCore V4 component-based format. Supported formats include KITTI, nuScenes,
9+
Waymo, COLMAP (including ScanNet++), and PAI.
1010

1111
.. toctree::
1212
:maxdepth: 1
1313

1414
kitti/kitti
15+
nuscenes/nuscenes
1516
waymo/waymo
1617
colmap/colmap
1718
pai/pai
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
.. SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
.. SPDX-License-Identifier: Apache-2.0
3+
4+
nuScenes Dataset
5+
================
6+
7+
The NCore nuScenes tool converts data from the
8+
`nuScenes <https://www.nuscenes.org/>`_ autonomous driving dataset into
9+
NCore V4 format. All dataset versions are supported (v1.0-mini,
10+
v1.0-trainval, v1.0-test).
11+
12+
.. _nuscenes_data_conventions:
13+
14+
Conventions
15+
-----------
16+
17+
The nuScenes dataset provides data from 6 cameras, 1 lidar, and 5
18+
radars. The converter handles all sensor modalities and 3D annotations.
19+
20+
Camera Sensors
21+
^^^^^^^^^^^^^^
22+
1. **Front (camera_front)** -- 1600x900, 70 deg FOV
23+
2. **Front Left (camera_front_left)** -- 1600x900, 70 deg FOV
24+
3. **Front Right (camera_front_right)** -- 1600x900, 70 deg FOV
25+
4. **Back (camera_back)** -- 1600x900, 110 deg FOV
26+
5. **Back Left (camera_back_left)** -- 1600x900, 70 deg FOV
27+
6. **Back Right (camera_back_right)** -- 1600x900, 70 deg FOV
28+
29+
All cameras use Basler acA1600-60gc sensors (global shutter). Images are
30+
provided undistorted with zero distortion coefficients. Camera intrinsics
31+
are stored using :class:`~ncore.data.OpenCVPinholeCameraModelParameters`
32+
with ``ShutterType.GLOBAL``.
33+
34+
LiDAR Sensor
35+
^^^^^^^^^^^^^
36+
1. **Top LiDAR (lidar_top)** -- Velodyne HDL-32E, 32 layers, ~34k points/frame
37+
38+
Point clouds in nuScenes are motion-compensated to the sensor frame at the
39+
sweep reference timestamp. The converter decompensates them back to
40+
per-point-time sensor frames (raw measurements) before storing, since
41+
NCore V4 expects non-motion-compensated ray-bundle data.
42+
43+
Per-point timestamps are derived from the column structure of the .bin
44+
file: each file contains 32-point columns (one per beam) in sequential
45+
firing order. All points within a column receive the same timestamp.
46+
Frame time bounds are derived from consecutive sweep timestamps (not a
47+
hardcoded scan frequency).
48+
49+
A structured lidar model (``RowOffsetStructuredSpinningLidarModelParameters``)
50+
is derived from the first frame's geometry and stored as intrinsics:
51+
52+
- ``row_elevations_rad``: per-beam elevation angles (median across columns)
53+
- ``column_azimuths_rad``: per-column azimuth angles (median across beams)
54+
- ``spinning_direction``: clockwise ("cw")
55+
- ``spinning_frequency_hz``: derived from inter-sweep timestamps (~20 Hz)
56+
57+
The ``model_element`` field is populated with ``[ring_index, column_index]``
58+
per point, enabling structured lidar operations at read time.
59+
60+
The minimum distance filter (1.0 m) matches the ``remove_close``
61+
default used by the nuScenes devkit to discard sensor housing
62+
reflections.
63+
64+
Radar Sensors
65+
^^^^^^^^^^^^^
66+
1. **Front (radar_front)** -- Continental ARS 408
67+
2. **Front Left (radar_front_left)** -- Continental ARS 408
68+
3. **Front Right (radar_front_right)** -- Continental ARS 408
69+
4. **Back Left (radar_back_left)** -- Continental ARS 408
70+
5. **Back Right (radar_back_right)** -- Continental ARS 408
71+
72+
Radar detections are sparse (typically 10-100 per sweep). Each detection
73+
provides position (x, y, z), ego-motion-compensated velocity, and radar
74+
cross section (RCS). Per-frame generic data fields:
75+
76+
- ``radial_velocity_m_s`` (float32, [N]) -- radial velocity in m/s
77+
(positive = moving away from sensor), computed by projecting the
78+
ego-motion-compensated velocity vector onto the detection direction.
79+
- ``rcs_dBsm`` (float32, [N]) -- radar cross section in dBsm.
80+
81+
Radar is not a spinning sensor; all detections in a frame share a single
82+
timestamp.
83+
84+
Ego Poses
85+
^^^^^^^^^
86+
Ego poses are derived from the per-sweep ``ego_pose`` records in the
87+
nuScenes database (GPS/INS-based). Poses are stored as dynamic
88+
``("rig", "world")`` poses relative to the first frame. The absolute
89+
first-frame pose is preserved as a static ``("world", "world_global")``
90+
transform.
91+
92+
3D Annotations
93+
^^^^^^^^^^^^^^
94+
Cuboid annotations are stored in the ``world_global`` coordinate frame
95+
(the nuScenes global map frame) as
96+
:class:`~ncore.data.v4.CuboidsComponent` observations. Only keyframe
97+
annotations are included. The :meth:`~ncore.data.CuboidTrackObservation.transform`
98+
method can re-project them to any sensor frame at runtime via the pose
99+
graph.
100+
101+
Category mapping from nuScenes to NCore class IDs:
102+
103+
- vehicle.car -> car
104+
- vehicle.truck -> truck
105+
- vehicle.bus.* -> bus
106+
- vehicle.construction -> construction_vehicle
107+
- vehicle.motorcycle -> motorcycle
108+
- vehicle.bicycle -> bicycle
109+
- vehicle.trailer -> trailer
110+
- vehicle.emergency.* -> emergency_vehicle
111+
- human.pedestrian.* -> pedestrian
112+
- movable_object.barrier -> barrier
113+
- movable_object.trafficcone -> traffic_cone
114+
115+
Usage
116+
-----
117+
118+
.. code-block:: bash
119+
120+
bazel run //tools/data_converter/nuscenes -- \
121+
--root-dir /path/to/nuscenes \
122+
--output-dir /path/to/output \
123+
nuscenes \
124+
--version v1.0-trainval
125+
126+
Convert a single scene by name:
127+
128+
.. code-block:: bash
129+
130+
bazel run //tools/data_converter/nuscenes -- \
131+
--root-dir /path/to/nuscenes \
132+
--output-dir /path/to/output \
133+
nuscenes \
134+
--version v1.0-mini \
135+
--scene-name scene-0061
136+
137+
See ``tools/data_converter/nuscenes/README.md`` for full option documentation.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
load("@ncore_pip_deps//:requirements.bzl", "requirement")
17+
load("@rules_python//python:defs.bzl", "py_binary", "py_library")
18+
load("//bazel/pytest:defs.bzl", "pytest_test")
19+
20+
# nuScenes-specific utilities: scene enumeration, timestamp estimation, box retrieval
21+
py_library(
22+
name = "pylib_utils",
23+
srcs = ["utils.py"],
24+
deps = [
25+
requirement("numpy"),
26+
requirement("nuscenes-devkit"),
27+
requirement("pyquaternion"),
28+
"//ncore:pylib",
29+
],
30+
)
31+
32+
# Converter library (config, converter class, CLI registration)
33+
py_library(
34+
name = "pylib",
35+
srcs = [
36+
"converter.py",
37+
],
38+
deps = [
39+
":pylib_utils",
40+
requirement("click"),
41+
requirement("numpy"),
42+
requirement("nuscenes-devkit"),
43+
requirement("pyquaternion"),
44+
requirement("tqdm"),
45+
requirement("universal_pathlib"),
46+
"//ncore:pylib",
47+
"//tools/data_converter:pylib_cli",
48+
],
49+
)
50+
51+
# Standalone CLI binary
52+
py_binary(
53+
name = "convert",
54+
srcs = ["main.py"],
55+
main = "main.py",
56+
deps = [":pylib"],
57+
)
58+
59+
alias(
60+
name = "nuscenes",
61+
actual = ":convert",
62+
)
63+
64+
# Integration test for the nuScenes converter (requires NUSCENES_DIR env var)
65+
pytest_test(
66+
name = "pytest_converter",
67+
srcs = ["converter_test.py"],
68+
args = ["--import-mode=importlib"],
69+
python_versions = ["3.11"],
70+
tags = ["manual"], # Only run when explicitly requested (needs external data)
71+
deps = [
72+
":pylib",
73+
requirement("nuscenes-devkit"),
74+
requirement("numpy"),
75+
requirement("parameterized"),
76+
requirement("pyquaternion"),
77+
requirement("torch"),
78+
requirement("universal_pathlib"),
79+
"//ncore:pylib",
80+
],
81+
)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
nuScenes Dataset
2+
Copyright (c) 2019 nuScenes (https://www.nuscenes.org)
3+
4+
This converter processes data from the nuScenes dataset.
5+
The nuScenes dataset is released under the Creative Commons
6+
Attribution-NonCommercial-ShareAlike 4.0 International License (CC BY-NC-SA 4.0).
7+
8+
Users must agree to the nuScenes Terms of Use before downloading or using the dataset:
9+
https://www.nuscenes.org/terms-of-use
10+
11+
The nuscenes-devkit library is released under the Apache License 2.0.
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# nuScenes to NCore V4 Converter
2+
3+
Converts [nuScenes](https://www.nuscenes.org/) dataset scenes to NCore V4 format.
4+
5+
## Requirements
6+
7+
- nuScenes dataset downloaded locally (any version: v1.0-mini, v1.0-trainval, v1.0-test)
8+
- Python packages: `nuscenes-devkit`, `pyquaternion`
9+
10+
## Usage
11+
12+
```bash
13+
bazel run //tools/data_converter/nuscenes -- \
14+
--root-dir /path/to/nuscenes \
15+
--output-dir /path/to/output \
16+
nuscenes \
17+
--version v1.0-trainval
18+
```
19+
20+
### Convert a single scene by token
21+
22+
```bash
23+
bazel run //tools/data_converter/nuscenes -- \
24+
--root-dir /path/to/nuscenes \
25+
--output-dir /path/to/output \
26+
nuscenes \
27+
--version v1.0-mini \
28+
--scene-token cc8c0bf57f984915a77078b10eb33198
29+
```
30+
31+
### Convert a single scene by name
32+
33+
```bash
34+
bazel run //tools/data_converter/nuscenes -- \
35+
--root-dir /path/to/nuscenes \
36+
--output-dir /path/to/output \
37+
nuscenes \
38+
--version v1.0-mini \
39+
--scene-name scene-0061
40+
```
41+
42+
## Options
43+
44+
| Option | Default | Description |
45+
|--------|---------|-------------|
46+
| `--version` | v1.0-trainval | nuScenes version string |
47+
| `--scene-token` | None | Filter to a single scene by token |
48+
| `--scene-name` | None | Filter to a single scene by name |
49+
| `--store-type` | itar | Output store format (itar or directory) |
50+
| `--profile` | separate-sensors | Component group assignment profile |
51+
| `--sequence-meta/--no-sequence-meta` | enabled | Generate sequence meta JSON |
52+
53+
## Sensor Assumptions
54+
55+
- **Cameras**: Treated as global shutter (ShutterType.GLOBAL). nuScenes provides a single
56+
capture timestamp per image with no rolling-shutter metadata. Images are already undistorted,
57+
so all distortion coefficients are zero.
58+
- **Lidar**: Velodyne HDL-32E spinning lidar at 20 Hz. Source point clouds are
59+
motion-compensated; the converter decompensates them to raw per-point-time
60+
measurements. Per-point timestamps are derived from the 32-beam column structure
61+
in the .bin file (robust to MC-induced spatial distortion). A structured lidar
62+
model (elevation/azimuth per beam/column) is derived from the first frame and
63+
stored as intrinsics.
64+
- **Cuboid annotations**: Stored in the world coordinate frame. Only keyframe annotations
65+
are included.
66+
67+
## Testing
68+
69+
```bash
70+
NUSCENES_DIR=/path/to/nuscenes NUSCENES_VERSION=v1.0-mini \
71+
bazel test //tools/data_converter/nuscenes:pytest_converter
72+
```

0 commit comments

Comments
 (0)