This directory contains the two-stage pipeline for creating the processed Waymo Open Dataset, starting from the raw TFRecord files on local disk and producing a feature-rich WebDataset ready for training and evaluation.
[Local: raw .tfrecord files] ──(to_intermediate.py)──► [Local: intermediate .tar] ──(to_webdataset_loftup.py)──► [Local: WebDataset + LoftUp features]
conda env: waymo conda env: vernata
You need to build two separate versions of the dataset:
| Variant | Output directory | Stage 1 --grid_size |
Stage 2 --no_image_features |
Used for |
|---|---|---|---|---|
| SSL pretraining | waymo_loftup_webdataset |
0.1 |
(omit — features extracted) | SSL pretraining with LoftUp image feature distillation |
| Downstream segmentation | waymo_webdataset_hr |
none |
✓ | Linear probing / supervised fine-tuning (full-resolution geometry) |
- The SSL pretraining variant voxel-downsamples to 0.1 m in Stage 1 and extracts DINOv2/LoftUp image features in Stage 2. Since training uses the same grid size anyway, pre-voxelizing avoids storing redundant points and — more importantly — keeps the expensive LoftUp features compact.
- The downstream segmentation variant stores the full-resolution point cloud
(
--grid_size none) because validation metrics are computed on the full cloud: the dataloader voxelizes at load time, but then assigns each voxel's predicted class back to every original point in that voxel. Pre-voxelizing would silently discard points and produce incorrect validation scores. Image features are skipped entirely (--no_image_features), which also makes processing much faster and the shards significantly smaller.
⚠️ Do not use the SSL pretraining dataset for downstream segmentation. Validation requires the full-resolution cloud (the dataloader voxelizes, but maps predictions back to every point per voxel). A pre-voxelized dataset would discard points and produce incorrect metrics.
Optional: When training without image feature distillation you can also create a
waymo_webdatasetvariant by passing--no_image_featureswith--grid_size 0.1. This produces significantly smaller shards than the LoftUp variant and is faster to load.
⚠️ Two separate conda environments are required. The Waymo Open Dataset SDK depends on TensorFlow 2.11 and Python 3.10, which are incompatible with the rest of the project stack. Stage 1 must therefore run in the dedicatedwaymoenvironment, while Stage 2 uses thevernataenvironment.
Stage Script Conda env Python Key dependencies 1 to_intermediate.pywaymo3.10 tensorflow 2.11,waymo-open-dataset-tf-2-11-0 1.6.1,open3d,webdataset2 to_webdataset_loftup.pyvernata3.12 torch 2.5,loftup,open3d,webdataset
For each segment (a .tfrecord file in the Waymo Open Dataset), this script:
- Lists all
.tfrecordfiles under<source_local_dir>/<split>/for each requested split (training,validation,testing). - Reads each
.tfrecordfile. - Parses each frame using the Waymo Open Dataset SDK:
- Decodes LiDAR range images from both return 1 and return 2 into Cartesian 3D coordinates, retaining per-point polar features (range, intensity, elongation).
- Decodes the 5 camera images (FRONT, FRONT_LEFT, FRONT_RIGHT, SIDE_LEFT, SIDE_RIGHT) and saves them as PNG.
- For
training/validation: skips frames that lack 3D semantic segmentation labels. - For
testing: skips frames not present in3d_semseg_test_set_frames.txt.
- Processes each frame's point cloud:
- Translates all points 1.5 m downward (to center the coordinate frame above the road surface).
- Estimates surface normals using Open3D (radius 0.5 m, max 30 neighbours), oriented toward the sensor origin.
- Applies voxel downsampling (
--grid_size) cropped to ±80 m (XY) / ±10 m (Z). - Computes per-point RGB colors by projecting downsampled points onto the camera images using the Waymo camera projection data.
- Saves the following arrays into a per-segment WebDataset
.tarshard:coord.npy— (N, 3) downsampled point coordinatesnormal.npy— (N, 3) estimated surface normalsstrength.npy— (N, 1) tanh-compressed LiDAR intensitycolor.npy— (M, 3) RGB colors for the colored subset of pointsprojections.npy— (N, 6) raw Waymo camera projection indices and (u, v) coordinateshas_color_index.npy— indices intocoordof the colored pointsdownsampled_index.npy— indices from the original cloud kept after downsamplingsegment.npy— (N,) semantic segmentation labels (training/validation only)pose.npy— 4×4 world-from-vehicle transformation matrix{CAMERA_NAME}.png— the 5 decoded camera images
- Creates a success marker (
.{segment_name}.success) so that already-processed segments are skipped on re-runs. - Writes an index file (
waymo_processed_index.txt) listing everysplit/segment/timestampentry.
SSL pretraining:
conda activate waymo
export WAYMO_OUTPUT_FOLDER=/path/to/waymo
python to_intermediate.py \
--source_local_dir "/path/to/waymo_tfrecords" \
--output_dir "$WAYMO_OUTPUT_FOLDER" \
--splits training validation \
--num_workers 32 \
--grid_size 0.1 \
--verboseDownstream segmentation (full-resolution cloud — required for correct validation):
conda activate waymo
export WAYMO_OUTPUT_FOLDER=/path/to/waymo
python to_intermediate.py \
--source_local_dir "/path/to/waymo_tfrecords" \
--output_dir "${WAYMO_OUTPUT_FOLDER}_no_grid" \
--splits training validation \
--num_workers 32 \
--grid_size none \
--verbose| Argument | Default | Description |
|---|---|---|
--source_local_dir |
(required) | Local directory containing split sub-directories with .tfrecord files. |
--output_dir |
(required) | Local base directory. Intermediate .tar shards and the index file are written to <output_dir>/waymo_intermediate/, with sub-directories training/, validation/, testing/ created automatically. |
--index_file |
waymo_processed_index.txt |
Filename for the final index listing all processed frame keys. |
--splits |
(required) | One or more of training, validation, testing. |
--num_workers |
cpu_count() |
Number of parallel worker processes (one segment per worker). |
--grid_size |
0.05 |
Voxel size (metres) for point cloud downsampling. Pass none to skip downsampling entirely. |
--verbose |
False |
Enable INFO-level logging. |
--partial |
False |
If set, process only the first segment per split. Useful for testing the pipeline end-to-end. |
<output_dir>/
waymo_intermediate/
training/
segment-12345abcde.tar ← one per TFRecord
.segment-12345abcde.success ← marker; segment is skipped on re-run
...
validation/
...
waymo_processed_index.txt ← lists all "split/segment/timestamp" entries
Each .tar shard contains one sample per Waymo frame, keyed by its
timestamp_micros.
Reads the intermediate per-segment .tar files produced by Stage 1 and adds DINOv2/LoftUp
visual features to each frame in a single streaming pass:
- Scans
<source_dir>/<split>/segment-*.tarfor each requested split. - Reads each frame's existing data from the intermediate
.tar:coord.npy,projections.npy,has_color_index.npyfor feature projection.- Five camera PNG images (FRONT, FRONT_LEFT, FRONT_RIGHT, SIDE_LEFT, SIDE_RIGHT).
- Converts the Waymo (N, 6) projection format into the (5, N, 3)
(valid, u, v)format expected by LoftUp's feature projection logic. - Extracts LoftUp features for each frame:
- Each camera image is resized individually (preserving aspect ratio) to the nearest multiple of the DINOv2 patch size, then passed through the chosen featurizer to produce low-resolution patch features.
- The LoftUp upsampler network maps these to high-resolution per-pixel features.
- Features are projected onto 3D points by bilinear sampling at the projected (u, v) pixel coordinates. Points visible in multiple cameras are overwritten in FRONT → FRONT_LEFT → FRONT_RIGHT → SIDE_LEFT → SIDE_RIGHT priority order (earlier cameras take precedence via reverse-iteration overwrite).
- Filters the dense (N, D) feature array down to only the colored points
(
has_color_index) to match the shape ofcolor.npy, producing a sparse (M, D)point_features.npy. - Writes a new
.tarfor each segment into<output_dir>/<split>/, containing all original data pluspoint_features.npy. - Creates a success marker so re-runs skip already-finished segments.
- Writes an index file (
waymo_loftup_index.txt) listing everysplit/segment/timestampentry across all processed splits.
SSL pretraining (LoftUp image features required):
conda activate vernata
export WAYMO_OUTPUT_FOLDER=/path/to/waymo
python to_webdataset_loftup.py \
--source_dir "${WAYMO_OUTPUT_FOLDER}/waymo_intermediate" \
--output_dir "${WAYMO_OUTPUT_FOLDER}/waymo_loftup_webdataset" \
--splits training validation \
--num_workers 2 \
--featurizer_class "dinov2s_reg" \
--verboseDownstream segmentation (geometry only, no image features needed):
Image features are not used during downstream segmentation, so skipping LoftUp extraction
results in significantly faster processing, smaller output shards, and faster data loading
during training. Use the intermediate dataset produced by the downstream Stage 1 command
above (with --grid_size none).
conda activate vernata
export WAYMO_OUTPUT_FOLDER=/path/to/waymo
python to_webdataset_loftup.py \
--source_dir "${WAYMO_OUTPUT_FOLDER}_no_grid/waymo_intermediate" \
--output_dir "${WAYMO_OUTPUT_FOLDER}_no_grid/waymo_webdataset_hr" \
--splits training validation \
--num_workers 8 \
--no_image_features \
--verbose| Argument | Default | Description |
|---|---|---|
--source_dir |
(required) | Root directory of the Stage 1 intermediate output (contains training/, validation/ sub-directories). |
--output_dir |
(required) | Local directory for output WebDataset shards with LoftUp features. |
--splits |
(required) | One or more of training, validation, testing. |
--index_file |
waymo_loftup_index.txt |
Filename for the final index listing all processed frame keys. |
--num_workers |
cpu_count() |
Number of parallel worker processes. Set low (e.g., 2) when GPU memory is the bottleneck. |
--featurizer_class |
dinov2s_reg |
DINOv2 backbone variant to use with LoftUp. Choices: dinov2s, dinov2b, dinov2s_reg, dinov2b_reg. |
--no_image_features |
False |
Skip LoftUp feature extraction and simply copy each sample as-is. Use this for downstream fine-tuning datasets (see above). |
--verbose |
False |
Enable INFO-level logging. |
--debug |
False |
Run in single-process mode (no ProcessPoolExecutor) and load models in the main process. Useful for step-through debugging. |
<output_dir>/
training/
segment-12345abcde.tar ← original data + point_features.npy
.segment-12345abcde.success
...
validation/
...
waymo_loftup_index.txt ← lists all "split/segment/timestamp" entries
Each sample in the output .tar contains all fields from Stage 1 plus:
point_features.npy— (M, D) LoftUp feature vectors for the colored points (aligned withcolor.npyandhas_color_index.npy)
export WAYMO_DATA_ROOT=/path/to/waymo_ssl
# ── Stage 1: parse TFRecords and create intermediate dataset ──────────────────
conda activate waymo
python to_intermediate.py \
--source_local_dir "/path/to/waymo_tfrecords" \
--output_dir "$WAYMO_DATA_ROOT" \
--splits training validation \
--num_workers 32 \
--grid_size 0.1 \
--verbose
# ── Stage 2: add LoftUp features ─────────────────────────────────────────────
conda activate vernata
python to_webdataset_loftup.py \
--source_dir "${WAYMO_DATA_ROOT}/waymo_intermediate" \
--output_dir "${WAYMO_DATA_ROOT}/waymo_loftup_webdataset" \
--splits training validation \
--num_workers 2 \
--featurizer_class "dinov2s_reg" \
--verboseexport WAYMO_DATA_ROOT=/path/to/waymo_ft
# ── Stage 1: parse TFRecords at full resolution (no voxel downsampling) ───────
conda activate waymo
python to_intermediate.py \
--source_local_dir "/path/to/waymo_tfrecords" \
--output_dir "${WAYMO_DATA_ROOT}_no_grid" \
--splits training validation \
--num_workers 32 \
--grid_size none \
--verbose
# ── Stage 2: copy data without LoftUp features ───────────────────────────────
conda activate vernata
python to_webdataset_loftup.py \
--source_dir "${WAYMO_DATA_ROOT}/waymo_intermediate" \
--output_dir "${WAYMO_DATA_ROOT}/waymo_webdataset_hr" \
--splits training validation \
--num_workers 8 \
--no_image_features \
--verbose