Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
910 changes: 776 additions & 134 deletions MODULE.bazel.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions deps/pip/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ pip_compile(
":requirements_colmap.in",
":requirements_docs.in",
":requirements_ncore.in",
":requirements_nuscenes.in",
":requirements_pai.in",
":requirements_tests.in",
":requirements_tools.in",
Expand Down
1 change: 1 addition & 0 deletions deps/pip/requirements_3_11.in
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
-r requirements_tools.in
-r requirements_waymo.in
-r requirements_colmap.in
-r requirements_nuscenes.in
-r requirements_pai.in

# Public API restrictions for 3.11
Expand Down
242 changes: 180 additions & 62 deletions deps/pip/requirements_3_11.txt

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions deps/pip/requirements_nuscenes.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# nuScenes converter dependencies
nuscenes-devkit
# pyquaternion is a transitive dependency of nuscenes-devkit that we also use directly
# for quaternion-to-rotation-matrix conversion and SLERP interpolation.
pyquaternion
5 changes: 3 additions & 2 deletions docs/conversions/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ Data Conversions
================

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

.. toctree::
:maxdepth: 1

kitti/kitti
nuscenes/nuscenes
waymo/waymo
colmap/colmap
pai/pai
137 changes: 137 additions & 0 deletions docs/conversions/nuscenes/nuscenes.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
.. SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
.. SPDX-License-Identifier: Apache-2.0

nuScenes Dataset
================

The NCore nuScenes tool converts data from the
`nuScenes <https://www.nuscenes.org/>`_ autonomous driving dataset into
NCore V4 format. All dataset versions are supported (v1.0-mini,
v1.0-trainval, v1.0-test).

.. _nuscenes_data_conventions:

Conventions
-----------

The nuScenes dataset provides data from 6 cameras, 1 lidar, and 5
radars. The converter handles all sensor modalities and 3D annotations.

Camera Sensors
^^^^^^^^^^^^^^
1. **Front (camera_front)** -- 1600x900, 70 deg FOV
2. **Front Left (camera_front_left)** -- 1600x900, 70 deg FOV
3. **Front Right (camera_front_right)** -- 1600x900, 70 deg FOV
4. **Back (camera_back)** -- 1600x900, 110 deg FOV
5. **Back Left (camera_back_left)** -- 1600x900, 70 deg FOV
6. **Back Right (camera_back_right)** -- 1600x900, 70 deg FOV

All cameras use Basler acA1600-60gc sensors (global shutter). Images are
provided undistorted with zero distortion coefficients. Camera intrinsics
are stored using :class:`~ncore.data.OpenCVPinholeCameraModelParameters`
with ``ShutterType.GLOBAL``.

LiDAR Sensor
^^^^^^^^^^^^^
1. **Top LiDAR (lidar_top)** -- Velodyne HDL-32E, 32 layers, ~34k points/frame

Point clouds in nuScenes are motion-compensated to the sensor frame at the
sweep reference timestamp. The converter decompensates them back to
per-point-time sensor frames (raw measurements) before storing, since
NCore V4 expects non-motion-compensated ray-bundle data.

Per-point timestamps are derived from the column structure of the .bin
file: each file contains 32-point columns (one per beam) in sequential
firing order. All points within a column receive the same timestamp.
Frame time bounds are derived from consecutive sweep timestamps (not a
hardcoded scan frequency).

A structured lidar model (``RowOffsetStructuredSpinningLidarModelParameters``)
is derived from the first frame's geometry and stored as intrinsics:

- ``row_elevations_rad``: per-beam elevation angles (median across columns)
- ``column_azimuths_rad``: per-column azimuth angles (median across beams)
- ``spinning_direction``: clockwise ("cw")
- ``spinning_frequency_hz``: derived from inter-sweep timestamps (~20 Hz)

The ``model_element`` field is populated with ``[ring_index, column_index]``
per point, enabling structured lidar operations at read time.

The minimum distance filter (1.0 m) matches the ``remove_close``
default used by the nuScenes devkit to discard sensor housing
reflections.

Radar Sensors
^^^^^^^^^^^^^
1. **Front (radar_front)** -- Continental ARS 408
2. **Front Left (radar_front_left)** -- Continental ARS 408
3. **Front Right (radar_front_right)** -- Continental ARS 408
4. **Back Left (radar_back_left)** -- Continental ARS 408
5. **Back Right (radar_back_right)** -- Continental ARS 408

Radar detections are sparse (typically 10-100 per sweep). Each detection
provides position (x, y, z), ego-motion-compensated velocity, and radar
cross section (RCS). Per-frame generic data fields:

- ``radial_velocity_m_s`` (float32, [N]) -- radial velocity in m/s
(positive = moving away from sensor), computed by projecting the
ego-motion-compensated velocity vector onto the detection direction.
- ``rcs_dBsm`` (float32, [N]) -- radar cross section in dBsm.

Radar is not a spinning sensor; all detections in a frame share a single
timestamp.

Ego Poses
^^^^^^^^^
Ego poses are derived from the per-sweep ``ego_pose`` records in the
nuScenes database (GPS/INS-based). Poses are stored as dynamic
``("rig", "world")`` poses relative to the first frame. The absolute
first-frame pose is preserved as a static ``("world", "world_global")``
transform.

3D Annotations
^^^^^^^^^^^^^^
Cuboid annotations are stored in the ``world_global`` coordinate frame
(the nuScenes global map frame) as
:class:`~ncore.data.v4.CuboidsComponent` observations. Only keyframe
annotations are included. The :meth:`~ncore.data.CuboidTrackObservation.transform`
method can re-project them to any sensor frame at runtime via the pose
graph.

Category mapping from nuScenes to NCore class IDs:

- vehicle.car -> car
- vehicle.truck -> truck
- vehicle.bus.* -> bus
- vehicle.construction -> construction_vehicle
- vehicle.motorcycle -> motorcycle
- vehicle.bicycle -> bicycle
- vehicle.trailer -> trailer
- vehicle.emergency.* -> emergency_vehicle
- human.pedestrian.* -> pedestrian
- movable_object.barrier -> barrier
- movable_object.trafficcone -> traffic_cone

Usage
-----

.. code-block:: bash

bazel run //tools/data_converter/nuscenes -- \
--root-dir /path/to/nuscenes \
--output-dir /path/to/output \
nuscenes \
--version v1.0-trainval

Convert a single scene by name:

.. code-block:: bash

bazel run //tools/data_converter/nuscenes -- \
--root-dir /path/to/nuscenes \
--output-dir /path/to/output \
nuscenes \
--version v1.0-mini \
--scene-name scene-0061

See ``tools/data_converter/nuscenes/README.md`` for full option documentation.
81 changes: 81 additions & 0 deletions tools/data_converter/nuscenes/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

load("@ncore_pip_deps//:requirements.bzl", "requirement")
load("@rules_python//python:defs.bzl", "py_binary", "py_library")
load("//bazel/pytest:defs.bzl", "pytest_test")

# nuScenes-specific utilities: scene enumeration, timestamp estimation, box retrieval
py_library(
name = "pylib_utils",
srcs = ["utils.py"],
deps = [
requirement("numpy"),
requirement("nuscenes-devkit"),
requirement("pyquaternion"),
"//ncore:pylib",
],
)

# Converter library (config, converter class, CLI registration)
py_library(
name = "pylib",
srcs = [
"converter.py",
],
deps = [
":pylib_utils",
requirement("click"),
requirement("numpy"),
requirement("nuscenes-devkit"),
requirement("pyquaternion"),
requirement("tqdm"),
requirement("universal_pathlib"),
"//ncore:pylib",
"//tools/data_converter:pylib_cli",
],
)

# Standalone CLI binary
py_binary(
name = "convert",
srcs = ["main.py"],
main = "main.py",
deps = [":pylib"],
)

alias(
name = "nuscenes",
actual = ":convert",
)

# Integration test for the nuScenes converter (requires NUSCENES_DIR env var)
pytest_test(
name = "pytest_converter",
srcs = ["converter_test.py"],
args = ["--import-mode=importlib"],
python_versions = ["3.11"],
tags = ["manual"], # Only run when explicitly requested (needs external data)
deps = [
":pylib",
requirement("nuscenes-devkit"),
requirement("numpy"),
requirement("parameterized"),
requirement("pyquaternion"),
requirement("torch"),
requirement("universal_pathlib"),
"//ncore:pylib",
],
)
11 changes: 11 additions & 0 deletions tools/data_converter/nuscenes/NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
nuScenes Dataset
Copyright (c) 2019 nuScenes (https://www.nuscenes.org)

This converter processes data from the nuScenes dataset.
The nuScenes dataset is released under the Creative Commons
Attribution-NonCommercial-ShareAlike 4.0 International License (CC BY-NC-SA 4.0).

Users must agree to the nuScenes Terms of Use before downloading or using the dataset:
https://www.nuscenes.org/terms-of-use

The nuscenes-devkit library is released under the Apache License 2.0.
72 changes: 72 additions & 0 deletions tools/data_converter/nuscenes/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# nuScenes to NCore V4 Converter

Converts [nuScenes](https://www.nuscenes.org/) dataset scenes to NCore V4 format.

## Requirements

- nuScenes dataset downloaded locally (any version: v1.0-mini, v1.0-trainval, v1.0-test)
- Python packages: `nuscenes-devkit`, `pyquaternion`

## Usage

```bash
bazel run //tools/data_converter/nuscenes -- \
--root-dir /path/to/nuscenes \
--output-dir /path/to/output \
nuscenes \
--version v1.0-trainval
```

### Convert a single scene by token

```bash
bazel run //tools/data_converter/nuscenes -- \
--root-dir /path/to/nuscenes \
--output-dir /path/to/output \
nuscenes \
--version v1.0-mini \
--scene-token cc8c0bf57f984915a77078b10eb33198
```

### Convert a single scene by name

```bash
bazel run //tools/data_converter/nuscenes -- \
--root-dir /path/to/nuscenes \
--output-dir /path/to/output \
nuscenes \
--version v1.0-mini \
--scene-name scene-0061
```

## Options

| Option | Default | Description |
|--------|---------|-------------|
| `--version` | v1.0-trainval | nuScenes version string |
| `--scene-token` | None | Filter to a single scene by token |
| `--scene-name` | None | Filter to a single scene by name |
| `--store-type` | itar | Output store format (itar or directory) |
| `--profile` | separate-sensors | Component group assignment profile |
| `--sequence-meta/--no-sequence-meta` | enabled | Generate sequence meta JSON |

## Sensor Assumptions

- **Cameras**: Treated as global shutter (ShutterType.GLOBAL). nuScenes provides a single
capture timestamp per image with no rolling-shutter metadata. Images are already undistorted,
so all distortion coefficients are zero.
- **Lidar**: Velodyne HDL-32E spinning lidar at 20 Hz. Source point clouds are
motion-compensated; the converter decompensates them to raw per-point-time
measurements. Per-point timestamps are derived from the 32-beam column structure
in the .bin file (robust to MC-induced spatial distortion). A structured lidar
model (elevation/azimuth per beam/column) is derived from the first frame and
stored as intrinsics.
- **Cuboid annotations**: Stored in the world coordinate frame. Only keyframe annotations
are included.

## Testing

```bash
NUSCENES_DIR=/path/to/nuscenes NUSCENES_VERSION=v1.0-mini \
bazel test //tools/data_converter/nuscenes:pytest_converter
```
Loading
Loading