Skip to content
Merged
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
2 changes: 1 addition & 1 deletion MODULE.bazel.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions deps/pip/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pip_compile(
size = "medium",
args = PIP_COMPILE_ARGS,
data = [
":requirements_argoverse2.in",
":requirements_colmap.in",
":requirements_docs.in",
":requirements_ncore.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 @@ -21,6 +21,7 @@
-r requirements_waymo.in
-r requirements_colmap.in
-r requirements_nuscenes.in
-r requirements_argoverse2.in
-r requirements_pai.in

# Public API restrictions for 3.11
Expand Down
4 changes: 3 additions & 1 deletion deps/pip/requirements_3_11.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1853,7 +1853,9 @@ pyarrow==23.0.1 \
--hash=sha256:f4b0dbfa124c0bb161f8b5ebb40f1a680b70279aa0c9901d44a2b5a20806039f \
--hash=sha256:fa8e51cb04b9f8c9c5ace6bab63af9a1f88d35c0d6cbf53e8c17c098552285e1 \
--hash=sha256:fed7020203e9ef273360b9e45be52a2a47d3103caf156a30ace5247ffb51bdbd
# via -r deps/pip/requirements_pai.in
# via
# -r deps/pip/requirements_argoverse2.in
# -r deps/pip/requirements_pai.in
pycocotools==2.0.11 \
--hash=sha256:04480330df5013f6edd94891a0ee8294274185f1b5093d1b0f23d51778f0c0e9 \
--hash=sha256:08c79789fd79e801ae4ecfcfeec32b31e36254e7a2b4019af28c104975d5e730 \
Expand Down
20 changes: 20 additions & 0 deletions deps/pip/requirements_argoverse2.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.

# Argoverse 2 converter dependencies.
# The converter reads the AV2 Feather files directly and intentionally avoids the
# heavy `av2` devkit (torch, kornia, numba, polars, PyAV). Quaternion handling uses
# scipy (already an ncore dependency), so no extra dependency is needed here.
pyarrow
135 changes: 135 additions & 0 deletions docs/conversions/argoverse2/argoverse2.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
.. SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
.. SPDX-License-Identifier: Apache-2.0

Argoverse 2 Dataset
===================

The NCore Argoverse 2 tool converts data from the
`Argoverse 2 <https://www.argoverse.org/av2.html>`_ Sensor Dataset into NCore
V4 format. The converter reads the Argoverse 2 on-disk Apache Feather files
directly with ``pyarrow`` and deliberately avoids the heavy ``av2`` devkit
(which pulls in torch, kornia, numba, polars and PyAV). Quaternion handling uses
``scipy`` (already an ncore dependency), so no extra dependency is introduced.

.. _argoverse2_data_conventions:

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

Argoverse 2 provides data from 9 cameras and 2 lidars; it has no radar. The
converter handles all sensor modalities and 3D cuboid annotations.

Camera Sensors
^^^^^^^^^^^^^^
1. **ring_front_center** -- 2048x1550 (portrait)
2. **ring_front_left** -- 1550x2048
3. **ring_front_right** -- 1550x2048
4. **ring_side_left** -- 1550x2048
5. **ring_side_right** -- 1550x2048
6. **ring_rear_left** -- 1550x2048
7. **ring_rear_right** -- 1550x2048
8. **stereo_front_left** -- 1550x2048
9. **stereo_front_right** -- 1550x2048

The released imagery for all nine cameras is already undistorted -- the official
av2 devkit projects with the intrinsic matrix ``K`` only and does not load the
distortion columns -- so camera intrinsics are stored using
:class:`~ncore.data.IdealPinholeCameraModelParameters`. Because the imagery is
already undistorted, **global shutter is assumed** (``ShutterType.GLOBAL``). The
``k1, k2, k3`` coefficients present in ``intrinsics.feather`` describe the
original lens (for re-distorting into the raw frame) and are intentionally not
applied to the released images; they are preserved per camera in the camera
component ``generic_meta_data`` under ``av2_original_distortion`` so the original
calibration is not lost.

LiDAR Sensors
^^^^^^^^^^^^^
1. **up_lidar** -- Velodyne VLP-32C, 32 beams, 10 Hz
2. **down_lidar** -- Velodyne VLP-32C, 32 beams, 10 Hz

Argoverse 2 sweeps are egomotion-compensated to the sweep reference timestamp
and provided in the egovehicle frame, with real per-point timestamps
(``offset_ns``). The two stacked VLP-32C units are stored separately, each with
its own static extrinsic. Points are split per unit by ``laser_number``,
mapped into the unit's own sensor frame, and decompensated using the real
per-point timestamps so that NCore stores raw per-point-time ray directions.
Because the sensor extrinsic is static, this decompensation is independent of
whether the source data applied ego-motion before or after the sensor
transform.

A structured VLP-32C model is stored per unit as lidar intrinsics, with per-point
``model_element`` (row, column). Argoverse 2 provides no native firing-column
index, so the firing pattern is reconstructed from ``offset_ns`` (firing columns --
one VLP-32C revolution at 10 Hz) and ``laser_number`` (the beam, mapped to an
elevation-sorted row). The geometry is derived per log from the *decompensated*
reference sweep: elevations, the laser-to-row map, column timing, per-column
azimuths, and per-row azimuth offsets (the 32 beams of a firing column span several
degrees of azimuth, so the per-row offset is fit empirically). The two stacked
units fire in opposite phase, so they spin oppositely in their own frames (one
``cw``, one ``ccw``), which is detected from the data. The column grid is upsampled
4x so per-frame alignment is not column-quantized, and each sweep is re-aligned to
the model by a per-frame affine column remap -- a constant phase (the spin phase at
a given ``offset_ns`` drifts ~1 deg between sweeps) plus a linear term (the spin
rate drifts slightly within a sweep on some scenes). Steep downward beams that only
return at near range (no far data) have their azimuth offset fit from near-range
returns. Deriving from the decompensated cloud (not the ego-motion-smeared
compensated one) plus these steps gives ~0.03 deg median far-range reconstruction
across scenes (validated on 38 val logs / 76 units, all sub-0.08 deg median with no
systematic azimuth or elevation bias), on par with native-column sensors. Pass
``--lidar-model-source none`` to store raw ray bundles only.

The ``laser_number`` to up/down unit split is not documented by Argoverse 2. The
two units occupy the two laser-number halves (``< 32`` and ``>= 32``); the unit
*label* is recovered from extrinsic geometry by per-beam elevation flatness -- a
laser ring traces a constant-elevation cone only in its own sensor frame, so the
wrong extrinsic tilts the cone and inflates the per-ring elevation spread. The
decision is made once per log and is stable with a wide (~2-10x) margin.

Annotations
^^^^^^^^^^^

3D cuboid annotations are native to the egovehicle frame at the sweep reference
time. They are stored in the ``rig`` frame at that timestamp with no ego pose
baked in, so the egovehicle motion stays out of the stored coordinates and
remains swappable downstream (a V4 feature); the pose graph places the cuboids
using the active ego trajectory. The full 3-DOF box orientation is preserved (the
AV2 quaternion is converted to the ``BBox3`` ``xyz``-Euler convention, not reduced
to yaw). The ``track_uuid`` is used as the track ID.

Coordinate Frames
^^^^^^^^^^^^^^^^^

The first ego pose's ``city_SE3_egovehicle`` is stored as the static
``world -> world_global`` pose, so ``world_global`` is the Argoverse 2 city
frame. All absolute city coordinates remain recoverable for later alignment
with the Argoverse 2 HD map (which the converter does not export).

Usage
-----

.. code-block:: bash

bazel run //tools/data_converter/argoverse2 -- \
--root-dir /path/to/argoverse2/sensor \
--output-dir /path/to/output \
argoverse2-v4 \
--split val

Convert a single log:

.. code-block:: bash

bazel run //tools/data_converter/argoverse2 -- \
--root-dir /path/to/argoverse2/sensor \
--output-dir /path/to/output \
argoverse2-v4 \
--split val \
--log-id 02678d04-cc9f-3148-9f95-1ba66347dff9

Testing
-------

.. code-block:: bash

AV2_DIR=/path/to/argoverse2/sensor AV2_SPLIT=val \
bazel test //tools/data_converter/argoverse2:pytest_converter
3 changes: 2 additions & 1 deletion docs/conversions/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ Data Conversions

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

.. toctree::
:maxdepth: 1

kitti/kitti
nuscenes/nuscenes
argoverse2/argoverse2
waymo/waymo
colmap/colmap
pai/pai
97 changes: 97 additions & 0 deletions tools/data_converter/argoverse2/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# 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")

# Argoverse 2 specific utilities: feather readers, sensor maps, lidar unit split
py_library(
name = "pylib_utils",
srcs = ["utils.py"],
deps = [
requirement("numpy"),
requirement("pyarrow"),
requirement("scipy"),
requirement("universal_pathlib"),
"//ncore:pylib",
"//tools/data_converter:pylib_structured_lidar_model",
],
)

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

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

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

# Integration test for the Argoverse 2 converter (requires AV2_DIR env var)
pytest_test(
name = "pytest_converter",
srcs = ["converter_test.py"],
python_versions = ["3.11"],
tags = ["manual"], # Only run when explicitly requested (needs external data)
deps = [
":pylib",
requirement("numpy"),
requirement("parameterized"),
requirement("pyarrow"),
requirement("torch"),
requirement("universal_pathlib"),
"//ncore:pylib",
],
)

# Data-free unit test for the VLP-32C lidar-model derivation (runs in CI).
pytest_test(
name = "pytest_utils",
srcs = ["utils_test.py"],
python_versions = ["3.11"],
deps = [
":pylib_utils",
requirement("numpy"),
requirement("pyarrow"),
requirement("torch"),
requirement("universal_pathlib"),
"//ncore:pylib",
],
)
12 changes: 12 additions & 0 deletions tools/data_converter/argoverse2/NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Argoverse 2 Dataset
Copyright (c) 2021 Argo AI, LLC

This converter processes data from the Argoverse 2 (AV2) Sensor Dataset.
The Argoverse 2 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 Argoverse Terms of Use before downloading or using the dataset:
https://www.argoverse.org/about.html#terms-of-use

This converter reads the Argoverse 2 on-disk Feather files directly and does not
depend on the av2 devkit. The av2 devkit is released under the MIT License.
Loading
Loading