Rust-native spatial computing
Point clouds · wgpu · COPC · RANSAC · ICP — native Rust, no C++ binding layer.
The hero GIF above is real MVP pipeline output (not a mockup): it uses the public PCL table_scene_lms400.pcd sample, voxel-downsamples it, RANSAC peels off the dominant plane, and Euclidean clustering lights up objects in color — every frame rendered straight from a live pipeline run.
| ⚡ GPU-accelerated | 🗂️ COPC-native | 🦀 Pure Rust | 🧩 Composable |
|---|---|---|---|
| wgpu voxel filter, ~3.9× at 2M points, automatic CPU fallback | bounds + LOD partial reads straight off disk — no full-tile load | no C++ / FFI binding layer to fight | one MVP crate: IO → filter → segment → register |
DBSCAN clustering and voxel occupancy grids, generated by examples/make_gifs.py through the Python bindings.
| Typical C++ stack (PCL / Open3D bindings) | SpatialRust | |
|---|---|---|
| Core language | C++ + FFI glue | Native Rust |
| GPU path | varies by wrapper | wgpu voxel filter with CPU fallback |
| COPC | bolt-on scripts | bounds + LOD queries in library & CLI |
| Pipeline | glue code | composable MVP crate |
One command from LAS/COPC to labeled clusters:
cargo run -p spatialrust --features mvp --bin spatialrust-mvp -- scan.las labeled.lasPartial COPC read + pipeline — stream only the region of interest straight off disk, no full-tile load:
cargo run -p spatialrust --features mvp --bin spatialrust-mvp -- \
--bounds 0,0,-1,100,100,1 --resolution 0.5 scan.copc.laz roi.copc.lazThe voxel downsampler runs on CPU or GPU (wgpu). ExecutionPolicy::Auto keeps small clouds on the CPU — where it's fastest — and switches to the GPU as point counts grow, so you get the best of both without tuning.
End-to-end centroid filter latency (leaf=4.0), measured via cargo bench -p spatialrust-filtering:
| Points | CPU | GPU | Winner |
|---|---|---|---|
| 100k | ~7 ms | ~17 ms | CPU |
| 200k | ~24 ms | ~26 ms | ~even |
| 500k | ~94 ms | ~51 ms | GPU |
| 1M | ~155 ms | ~56 ms | GPU (~2.8x) |
| 2M | ~389 ms | ~101 ms | GPU (~3.9x) |
Reproduce: cargo bench -p spatialrust-filtering --features filter-voxel-gpu --bench voxel_downsample.
Normal estimation has an optional wgpu path (GpuNormalEstimator, feature-normal-gpu). In radius mode the neighbor search runs entirely on the GPU via a uniform grid (covariance + Jacobi eigensolver included), which is up to ~50× faster than the CPU KD-tree estimator:
| Points | CPU (KD-tree) | GPU grid | Speedup |
|---|---|---|---|
| 100k | ~220 ms | ~8.6 ms | ~26× |
| 200k | ~442 ms | ~15 ms | ~29× |
| 500k | ~1.47 s | ~29 ms | ~50× |
(A k-nearest mode that keeps neighbor search on the CPU is also available but only ~1.1× — see notes.) Reproduce: cargo bench -p spatialrust-features --features feature-normal-gpu --bench normals.
A reproducible, apples-to-apples comparison against PCL 1.15.1 — both libraries process the same public PCL table_scene_lms400.pcd scan (460,400 points) with matching parameters (harness). Values below are from a local Windows release run using MSYS2 g++ 16.1.0 and vcpkg; rerun the harness before publishing fresh cross-machine numbers.
powershell -ExecutionPolicy Bypass -File bench\pcl_comparison\run.ps1| Operation | SpatialRust | PCL | |
|---|---|---|---|
| Radius Outlier Removal | 0.0899 s | 1.8784 s | 20.89× faster |
| Statistical Outlier Removal | 0.1664 s | 2.0933 s | 12.58× faster |
| Normal estimation (k=10) | 0.1461 s | 1.9750 s | 13.52× faster |
| Voxel downsample | 0.0104 s | 0.0181 s | 1.74× faster |
SpatialRust wins 4 of 4 against this PCL run; voxel downsampling now uses a specialized XYZ centroid path with compact u32 voxel keys for the common min-origin case.
An Open3D comparison harness is available at bench/open3d_comparison. It runs the same public PCL table_scene_lms400.pcd scan through SpatialRust and Open3D with matching voxel, normal, statistical outlier, and radius outlier parameters:
python bench/open3d_comparison/run.pyIndicative local result on one Windows machine (Open3D 0.19.0, Python 3.12, 460,400-point public PCL sample):
| Operation | SpatialRust | Open3D | |
|---|---|---|---|
| Voxel downsample | 0.0132 s | 0.0234 s | 1.77× faster |
| Normal estimation | 0.1997 s | 0.4946 s | 2.48× faster |
| Statistical Outlier Removal | 0.2105 s | 0.6565 s | 3.12× faster |
| Radius Outlier Removal | 0.1049 s | 66.4701 s | 633.65× faster |
Record CPU, Open3D version, Python version, and thread settings before publishing new numbers.
Four registration backends, compared on a synthetic box corner (7500 points, small misalignment):
| Method | Recovery error | Time | Notes |
|---|---|---|---|
| ICP (point-to-point) | 0.0196 m | ~147 ms | slow to converge on planar surfaces |
| Point-to-plane ICP | 0.0007 m | ~6.5 ms | best speed/accuracy balance |
| GICP | 0.0006 m | ~26 ms | most accurate; per-point covariance (optional GPU covariance ~1.7×, register-gicp-gpu) |
| NDT | 0.0008 m | ~8.7 ms | voxel distributions + Levenberg–Marquardt |
See notes. Reproduce: cargo bench -p spatialrust-registration --features register-icp,register-icp-point-to-plane,register-gicp,register-ndt --bench registration.
MVP pipeline is implemented end-to-end: PCD/PLY/LAS/COPC IO, voxel downsampling (CPU + optional wgpu), normals, RANSAC plane segmentation, Euclidean clustering, region growing, and registration (ICP point-to-point/point-to-plane, GICP, NDT). See docs/ARCHITECTURE.md for the master design.
One dataflow, eleven crates — each pipeline stage maps to the crate that implements it, all sitting on a small math/core/search foundation:
| Crate | Role |
|---|---|
spatialrust |
Meta crate / stable re-exports |
spatialrust-core |
Point schema, metadata, execution traits |
spatialrust-math |
Vec/Mat/Pose math primitives |
spatialrust-io |
Point cloud readers/writers (PCD, PLY, LAS, COPC) |
spatialrust-search |
KD-tree search, k-NN / radius graphs |
spatialrust-filtering |
Voxel / FPS downsample, outlier removal, crop, MLS |
spatialrust-features |
Normals (CPU + wgpu), ISS keypoints, FPFH, boundary, normal orientation |
spatialrust-segmentation |
RANSAC plane / sphere / cylinder, Euclidean, DBSCAN, region growing, ground |
spatialrust-registration |
ICP (point-to-point, point-to-plane), GICP, NDT, FPFH global |
spatialrust-transform |
Affine transforms, recenter / normalize, merge, AABB / OBB |
spatialrust-voxelize |
Voxel occupancy grids and LiDAR range images |
spatialrust-metrics |
Chamfer / Hausdorff cloud distances |
spatialrust-pipeline |
Composable MVP pipelines |
spatialrust-gpu |
wgpu runtime and voxel kernels |
The whole pipeline is callable from Python with NumPy interop — no C++ binding layer:
import numpy as np
import spatialrust as sr
cloud = sr.PointCloud.from_xyz(points) # (N, 3) float32 -> native cloud
result = sr.run_pipeline(cloud, leaf_size=0.1, cluster_tolerance=0.3)
print(result.plane_normal) # dominant plane normal (nx, ny, nz)
labels = result.labels() # (N,) int32 cluster ids
sr.write("labeled.las", result.output) # LAS/PCD/PLY/COPC by extensionRegistration is callable too — align two scans with ICP / point-to-plane / GICP / NDT:
result = sr.register_gicp(source, target) # also: register_icp / _point_to_plane / _ndt
T = result.transform() # 4x4 matrix mapping source -> targetAnd it's a preprocessing front-end for learned models — turn a scan into model-ready tensors in a few calls (clean → unit-sphere normalize → FPS → voxel grid / range image / k-NN edge_index):
sampled = sr.farthest_point_sampling(sr.normalize_unit_sphere(cloud), 2048)
occ, origin, vsize = sr.voxelize(sampled, voxel_size=0.06) # (nz, ny, nx) occupancy
edge_index = sr.knn_graph(sampled, k=16) # (2, E) PyG-style graph
rimg = sr.range_image(sampled, width=256, height=64) # (H, W) LiDAR depthGenerated by examples/ml_preprocess.py — see the Python README.
Build the extension with maturin and reproduce the Python previews from the same public sample:
pip install maturin numpy matplotlib
cd crates/spatialrust-py && maturin develop --release
mkdir -p ../../target/readme-data
curl -L --fail -o ../../target/readme-data/table_scene_lms400.pcd \
https://raw.githubusercontent.com/PointCloudLibrary/data/master/tutorials/table_scene_lms400.pcd
PUBLIC=../../target/readme-data/table_scene_lms400.pcd
python examples/segment_room.py \
--input "$PUBLIC" \
--leaf-size 0.03 --plane-distance 0.025 \
--cluster-tolerance 0.06 --min-cluster-size 8 \
--png ../../docs/assets/python_segmentation.png
python examples/register_scans.py \
--input "$PUBLIC" --leaf 0.05 \
--png ../../docs/assets/python_registration.png
python examples/ml_preprocess.py \
--input "$PUBLIC" \
--png ../../docs/assets/ml_preprocess.pngPrebuilt abi3 wheels (CPython 3.8+) are produced by CI and published to PyPI on tagged releases (pip install spatialrust). See crates/spatialrust-py/README.md for the full Python API.
cargo test --workspace
cargo test -p spatialrust --features mvp
cargo doc --workspace --opencargo run -p spatialrust --features mvp --bin spatialrust-mvp -- input.las output.las
cargo run -p spatialrust --features mvp --bin spatialrust-mvp -- \
--leaf-size 0.2 --voxel-policy auto scan.copc.laz out.copc.laz
cargo run -p spatialrust --features mvp --bin spatialrust-mvp -- \
--bounds 0,0,-1,100,100,1 scan.copc.laz roi.copc.laz
cargo run -p spatialrust --features mvp --bin spatialrust-mvp -- \
--bounds 0,0,-1,100,100,1 --resolution 0.5 scan.copc.laz roi.copc.laz
cargo run -p spatialrust --features mvp --bin spatialrust-mvp -- \
--resolution 0.5 scan.copc.laz coarse.copc.lazLoad or save by file extension:
use spatialrust::{read_point_cloud_file, write_point_cloud_file};
let cloud = read_point_cloud_file("scan.las")?;
write_point_cloud_file("output.ply", &cloud)?;COPC partial read:
use spatialrust::{read_copc_file_with_query, CopcBounds, CopcQuery};
let bounds = CopcBounds::from_ranges((0.0, 100.0), (0.0, 100.0), (-1.0, 1.0));
let cloud = read_copc_file_with_query("scan.copc.laz", &CopcQuery::bounds(bounds))?;PCD/PLY/LAS/COPC -> voxel downsample -> normals -> plane RANSAC -> clustering -> ICP -> save
GPU voxel downsampling (wgpu) is available behind features. ExecutionPolicy::Auto keeps CPU for clouds below ~500k points (centroid mode).
cargo test -p spatialrust-gpu --features gpu-wgpu
cargo test -p spatialrust --features filter-voxel-gpu
cargo test -p spatialrust --features mvp mvp_copc_pipeline_roundtrip
cargo test -p spatialrust --features mvp mvp_copc_query_pipelineThe main README pipeline visuals use the public PCL table_scene_lms400.pcd sample, cached under target/readme-data/ at generation time rather than committed to the repository. Regenerate them with:
cargo run -p spatialrust --features mvp --example readme_mvp_previewOutputs: readme_hero.gif (header), readme_mvp_preview.svg (pipeline panel), copc_query.gif (COPC partial read), benchmark_voxel.svg (Performance chart), architecture.svg (crates diagram), readme_mvp_pipeline.gif (compact 2D view), and social_preview.svg.
Use SPATIALRUST_README_CLOUD=/path/to/cloud.pcd to render the same assets from another local public dataset.
The rotating clusters_rotating.gif and voxelize_rotating.gif are generated through the Python bindings from the same public sample: python crates/spatialrust-py/examples/make_gifs.py --input target/readme-data/table_scene_lms400.pcd (needs maturin develop + Matplotlib/Pillow).
Upload docs/assets/social_preview.svg (or export to PNG) as the GitHub repository social image under Settings → General → Social preview.
Licensed under MIT OR Apache-2.0 at your option.







