How to load raw .npy and .bin point clouds with a layout¶
A .bin file is numbers. Only you know that KITTI's are float32 (N, 4) with x, y, z, reflectance in the sensor frame in metres. pardesline refuses to guess and takes a layout:
import pardesline as pl
sweep = pl.load(
"0000000000.bin",
layout={"dtype": "float32", "shape": "(N,4)", "columns": ["x", "y", "z", "reflectance"]},
).with_frame("sensor", "m")
print(sweep.kind, sweep.to_numpy().shape) # pointcloud (N, 3)
sweep.save("sweep.ply") # PLY with reflectance as a property + sidecar with the layout
A (N,k) shape is points, an (a,b,c) shape is a grid; kind="volume" or kind="labels" forces it. For .npz, key= picks the array; allow_pickle stays off.
occ = pl.load(
"frame.npz",
layout={"key": "occ_label", "shape": "(200,200,16)", "spacing": [0.4, 0.4, 0.4]},
kind="labels",
)
occ = occ.with_labels({0: "free", 1: "car", 2: "pedestrian"})
occ.to_mesh(label=1).save("cars.ply")
In a manifest, once for the whole dataset¶
The bundled KITTI manifest declares the layout on the role, so every sweep opens without arguments:
drive = pl.load("kitti_raw_2011_09_26_drive_0001")
frame = next(s for s in drive if "lidar" in s.roles)
sweep = frame.lidar # (N, 4) decoded through the manifest layout, sensor frame, metres
objects = sweep.run("lidar_scene_detection.object_clustering")
For your own folder of .bin files, pl.scan(folder) asks for the layout as a question; answer with layout={"lidar": {...}} or edit the manifest.
Checks¶
- Point count:
len(file) / (4 * 4)must be an integer forfloat32 (N,4); otherwise the dtype or the column count is wrong. - Scale: KITTI ranges reach 80 m; if
to_numpy().max()is 80 000 the file is in millimetres. - Order:
order="F"for Fortran-ordered grids (MATLAB exports).
Related: KITTI raw manifest · UniOcc occupancy grids · Concepts: object