lidarverse / utils /pai_batch.py
multimodalart's picture
multimodalart HF Staff
LidarVerse Stage-3 world generation demo
af8bc9f verified
Raw History Blame Contribute Delete
2.67 kB
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
"""Shared processed-PAI batch contract for training and inference."""
import numpy as np
import torch
def make_pai_batch(sample, length, *, input_frames=None, hdmap=True, boxes=True, device="cpu"):
ranges = sample["range_m"][:length]
valid = sample["valid_mask"][:length] & np.isfinite(ranges) & (ranges >= 2.5) & (ranges <= 100)
video = np.where(valid, 2 * (ranges - 2.5) / 97.5 - 1, -1).astype(np.float32)
if input_frames is not None:
video[input_frames:] = -1 # Never expose future GT during inference.
poses = sample["pose_sensor_start_end"][:length]
tensor = lambda value: torch.as_tensor(value, device=device)
batch = dict(
video=tensor(video)[None, None], is_preprocessed=True,
lidar_hdmap=tensor(sample["hdmap"][:length].astype(np.float32))[None, None]
if hdmap else torch.full((1, 1, length, 128, 1024), -1., device=device),
sensor_dataset_id=tensor([0]).long(), sensor_ray_directions=tensor(sample["ray_directions"])[None].float(),
sensor_depth_bounds=tensor([[2.5, 100.0]]).float(),
lidar_to_world=tensor(poses[:, 0])[None].float(),
pose_sensor_start_end=tensor(np.linalg.inv(poses[:, 1]) @ poses[:, 0])[None].float(),
fps=tensor([10.0]), num_frames=tensor([length]), image_size=tensor([[128, 1024]]),
padding_mask=torch.zeros(1, 1, 128, 1024, device=device),
t5_text_embeddings=torch.zeros(1, 512, 1024, device=device),
t5_text_mask=torch.zeros(1, 512, device=device),
)
batch["t5_text_mask"][:, 0] = 1
if boxes:
valid_boxes = sample["box_valid"][:length].astype(bool)
tracks = sample["box_track_id"][:length]
dynamic_ids = np.unique(tracks[valid_boxes & sample["box_is_moving"][:length]])
dynamic = valid_boxes & np.isin(tracks, dynamic_ids)
for key in ("object_to_world", "lwh", "track_id"):
array = sample[f"box_{key}"][:length]
packed = np.zeros((length, 256) + array.shape[2:], dtype=array.dtype)
if key == "track_id":
packed.fill(-1)
for t in range(length):
selected = array[t, dynamic[t]]
if len(selected) > 256:
raise ValueError("More than 256 dynamic boxes in a sweep")
packed[t, :len(selected)] = selected
batch[f"moving_box_{key}"] = tensor(packed)[None]
batch["moving_box_valid"] = tensor(np.arange(256)[None] < dynamic.sum(axis=1)[:, None])[None]
return batch