Source code for vision3d.transforms.functional._sweeps
"""Functional kernel for temporal lidar sweep accumulation."""
from collections.abc import Sequence
import torch
from torch import Tensor
[docs]
def accumulate_sweeps(
sweeps: Sequence[Tensor],
transforms: Tensor,
time_offsets: Tensor,
) -> Tensor:
"""Accumulate and time-stamp a set of lidar sweeps.
Each sweep is mapped into a common target frame by its own rigid
transform, then all sweeps are concatenated into a single point cloud
with a new trailing column holding the per-point time offset. This
densifies a sparse lidar frame by folding in neighbouring sweeps while
recording when each point was captured.
The transform is applied to the ``(x, y, z)`` coordinates only. Feature
columns (e.g. intensity) pass through unchanged and the time offset is
appended after them, so a sweep of shape ``[N, 3+C]`` contributes rows of
shape ``[N, 3+C+1]``.
Args:
sweeps: Sweeps to aggregate, each a ``[N_i, 3+C]`` point cloud whose
first three columns are ``(x, y, z)`` in that sweep's own frame,
followed by ``C`` feature columns. Every sweep must share the same
number of feature columns.
transforms: Rigid ``[S, 4, 4]`` homogeneous transforms, one per sweep,
mapping that sweep's coordinates into the target frame.
time_offsets: ``[S]`` per-sweep time offsets (e.g. seconds relative to
the target frame) broadcast into the appended column.
Returns:
Aggregated ``[sum(N_i), 3+C+1]`` point cloud in the target frame, with
the time offset as the last column. Rows follow the order of
``sweeps``.
Raises:
ValueError: If ``sweeps`` is empty, or if ``transforms`` or
``time_offsets`` do not have exactly one entry per sweep.
"""
if len(sweeps) == 0:
raise ValueError("accumulate_sweeps requires at least one sweep.")
if transforms.shape[0] != len(sweeps) or time_offsets.shape[0] != len(sweeps):
raise ValueError(
f"Expected one transform and time offset per sweep, got "
f"{len(sweeps)} sweeps, {transforms.shape[0]} transforms, and "
f"{time_offsets.shape[0]} time offsets."
)
compensated: list[Tensor] = []
for points, transform, dt in zip(sweeps, transforms, time_offsets):
transform = transform.to(points.dtype)
xyz = points[:, :3] @ transform[:3, :3].T + transform[:3, 3]
times = dt.to(points.dtype).reshape(1, 1).expand(points.shape[0], 1)
compensated.append(torch.cat([xyz, points[:, 3:], times], dim=1))
return torch.cat(compensated, dim=0)