New
Apps 1,895Makers 1,890This week 1,690
Promote your app−30%
0views in
the last 30 days

visloc-rs

SfM, visual-inertial SLAM, 3D Gaussian Splatting and map-based localization in Rust

Built with Claude Code

by rsasaki0109 · Unclaimed · from JPJapan · added Oct 29, 2026
no ratings0 comments

visloc-rs

Structure from Motion, visual-inertial SLAM, 3D Gaussian Splatting and map-based localization — in pure Rust.

Visual-inertial SLAM on LaMAria sequence_1_19 (Project Aria glasses, 1.0 km city walk). Left: both Aria SLAM cameras with the points the VIO is tracking. Right: top-down trajectories growing in time against the pseudo ground truth: visloc-rs (blue) and OpenVINS (orange) stay on the route, Basalt with its upstream configuration (red) drifts away.
Visual-inertial odometry on LaMAria sequence_1_19: Project Aria glasses, 1.0 km city walk, 15 min, both SLAM cameras + IMU, no loop closure. LaMAria Score (official evaluator, higher is better): visloc-rs 50.0, OpenVINS 49.9 (estimate shipped with the LaMAria demo), Basalt with its upstream configuration 7.7 (run through this repository's parity-verified Basalt port). Trajectories are Sim(3)-aligned to the pseudo ground truth as the evaluator does. Details and the other training sequences.

Objects in metres from one camera and an IMU on KITTI raw drive 0084. Top: the camera image with YOLOX boxes; each car is labelled with its height, its distance and its speed or 'parked'. Bottom left: a top view following the camera with a ring every 10 m, the cars' positions and the moving ones' paths. Bottom right: the whole drive from above; mono + IMU follows the ground truth, while the visual-only VO, drawn as metres, covers a few metres near the start; a readout gives the camera's own speed from each, tens of km/h from the ground truth and mono + IMU and under 1 km/h from the visual-only VO.
Objects in metres from one camera and an IMU, offline, on KITTI raw drive 0084. Each car's foot is where the bottom of its box meets the ground: the VIO's metric scale puts the camera a measured height above the ground, and gravity gives down. That gives the height, the distance and, from the foot's path, the speed. Against KITTI's 3D boxes on five drives, car heights come out 11 % off (median, 110 cars) with mono + IMU and 6 % with stereo + IMU; pedestrians 7 cm (stereo + IMU). Speed is the weak part: still cars 0.15 m/s, moving cars 2–3 m/s off. With the IMU off (bottom right), the visual-only VO draws the 219 m drive as 5.7 m, 39× too small. Details. Images: KITTI raw (CC BY-NC-SA 3.0).

Map-based localization on EuRoC: the 3D landmarks of a map saved by the stereo-inertial SLAM from the MH_01 flight, with the MH_02 flight's visual-inertial odometry corrected into its frame. Inset: the current camera image with the keypoints that matched the map in green. In the map: the current camera in red with green rays to the landmarks it matched, and its map-frame trajectory so far in orange, unbroken, tracing the flight's loops through the machine hall.
Map-based localization: EuRoC MH_02 in a map that the stereo-inertial SLAM saved from MH_01, a different flight. The map fixes correct the VIO into the map frame, so every frame gets a map pose (orange): 3,039 of the 3,040 frames, at 1.9 cm RMSE against ground truth. Green: the keypoints that matched the map, searched around where the corrected VIO predicts each landmark (98% of the images match; 81% when each image is matched alone), and rays to those landmarks. The same runs in real time as ROS 2 nodes. Details and the other EuRoC queries.

One continuous orbit camera circling a whole city block (Hierarchical 3DGS SmallCity) from high above the rooftops, a viewpoint no input photo has: the sparse SfM point cloud of the surrounding streets and the 5,583 recovered camera frustums tracing the bicycle's path around the block pop in, dissolve into the photoreal 3D Gaussian splat rendered with visloc-rs's own Rust + wgpu renderer, streets and parked cars included, then dissolve into the extracted mesh.
5,822 photos of a city block → 5,583 camera poses → 4.7M-gaussian splat → 5.9M-triangle mesh in 2.5 h, one gsplat_photos command, no COLMAP or Python, orbited from high above where no input photo was taken. Details.

In a nutshell

visloc-rs turns camera images (optionally with an IMU) into camera poses, 3D maps and photorealistic scenes, and localizes new images against those maps. Everything is Rust: no C++, OpenCV or CUDA toolchain to build. GPU work runs on any GPU through wgpu. Models are read and written in COLMAP format, so results plug into existing tools.

What Headline result (measured, real data) vs
Photo SfM (unordered images) ETH3D Electro 1,200 images: 3.46× faster, 25% lower camera-centre error; 9,996 / 10,008 cameras registered across all ten ETH3D many-view scenes COLMAP 3.9 CPU
GPU SfM (video frames) EuRoC: faster on 8/8 sequences (1.4–7.5×), more accurate on 4/8, equal on 1; COLMAP breaks on MH_05 (194 cm vs 2.6 cm) COLMAP 4.1 CUDA
Stereo-inertial VI-SLAM (Basalt port + online mapper) Real time on 11/11 EuRoC sequences on the dev machine (1.06–1.68×, thin margin on the slowest); beats ORB-SLAM3 on 9/11 ORB-SLAM3, Basalt
Egocentric VI-SLAM (Project Aria, both SLAM cameras + IMU) LaMAria, all 5 tracks: mean Score 64.3, above GroundedSLAM (62.3); ahead on Short, Medium and Long, level on Low light, behind on Moving; causal and real time on 4 CPU cores (training sequences; the leaderboard's are test) GroundedSLAM, Aria's SLAM
Metric objects (mono camera + IMU + a 2D detector) KITTI raw, against the 3D boxes: car heights 11 % off with mono + IMU (110 cars), 6 % with stereo + IMU; pedestrians 7 cm KITTI tracklets
Photos → 3D Gaussian Splatting + mesh Faster than brush on 5/5 benchmark scenes, PSNR within 0.05 dB on 4 brush 0.3
Localization against a prebuilt map OpenLORIS robot rig: 98.96% of 1,250 held-out frames localized, median 2.9 mm; simulated house: 38 ms / frame —
Stereo / RGB-D VO KITTI 00 1.23 m and 09 2.07 m (ORB-SLAM2: 1.3 m / 3.2 m); TUM fr1_xyz 1.4 cm ORB-SLAM2

Where it still loses, measured the same way: COLMAP CUDA is more accurate on 3 of the 8 EuRoC sequences and registers more of the blurred frames; ORB-SLAM3 wins 2 of 11 EuRoC sequences (MH_04, MH_05); on LaMAria, Aria's own closed-source SLAM stays well ahead (75.9) and GroundedSLAM leads the Moving-platform track; OpenLORIS 10k rig SfM is not yet at COLMAP's RMSE. Every number above links to a benchmark doc with the commands to reproduce it.

Contents

Sensor support

What each sensor setup can do today. Benchmarked means a measured real-data result is in the repo. Experimental means the code runs but the docs record open gaps.

Sensor setup Tasks Status Headline result Entry point
Monocular, unordered photos SfM, 3DGS, mesh Benchmarked ETH3D Electro 1,200: 3.46× faster than COLMAP CPU, 3.50 vs 4.68 cm unordered_sfm_demo, gsplat_photos
Monocular video SfM Benchmarked EuRoC: faster than COLMAP 4.1 CUDA on 8/8 sequences, more accurate on 4/8 gsplat_euroc
Posed photos (a COLMAP sparse model) Dense MVS Experimental ETH3D high-res training, 13 scenes at 3200 px on one L4: F1 83.7% at 2 cm in 74 min (ACMMP 83.42%), 4× faster than ACMMP's code on the same GPU; 81.5% end to end from the photos visloc-mvs example mvs (notes)
Monocular video VO (DPVO port) Experimental MH_01 prefix 0.16 m, about 2× DPVO's published error; CPU only euroc_dpvo_vo_demo
Monocular + IMU VIO (Basalt port) Experimental TUM-VI room1–6: metric scale within 3.4% (median 1.3%), ATE 0.02–0.19 m, at most 0.014 m above stereo + IMU; no EuRoC result yet basalt_euroc_vio_demo --mono (notes)
Monocular + IMU, plus a 2D detector Object height, distance and speed in metres Experimental KITTI raw, 5 drives: car heights 11 % off (median, 110 cars), distances 19 %; moving cars' speeds 2–3 m/s off objects_metric_offline (notes)
Stereo VO / SLAM Benchmarked KITTI seq00 1.23 m and seq09 2.07 m, vs 1.3 m and 3.2 m for ORB-SLAM2 deep_stereo_slam, online_slam_stereo_vo_kitti_demo
Stereo + IMU VIO + mapping (Basalt port) Benchmarked Beats ORB-SLAM3 on 9/11 EuRoC sequences; native-Basalt parity within 0.1%; real time (RTF 1.06–1.68) on all 11/11 EuRoC sequences on the dev machine, thin margin on the slowest basalt_euroc_online_slam_demo
RGB-D (as virtual stereo) VO Benchmarked TUM fr1_xyz 0.014 m, fr1_desk 0.026 m (about 1.3–1.6× ORB-SLAM2 RGB-D) TUM RGB-D
Multi-camera rig SfM Experimental OpenLORIS 10k: 9,998/10,000 registered; RMSE parity with COLMAP still open generalized_rig_sfm
Robot rig cameras vs. a prebuilt map Relocalization Benchmarked OpenLORIS (robot-mounted rig): 98.96% of 1,250 held-out frames localized against a map built from other frames, median 2.9 mm localize_openloris_map
Wheeled robot: stereo + IMU (RealSense T265), wheel odometry Localization in a saved SLAM map, odometry corrected into its frame Benchmarked OpenLORIS-Scene office, other sessions up to 2.8 days later with furniture moved: VIO + map gives 94–99.8% of frames a map pose at 2.8–7.6 cm, and 1.1–2.4 cm with the stereo baseline corrected from the wheel odometry; wheel odometry + map 3.5–5.1 cm with its reference point and level calibrated from the VIO, and the same through the ROS 2 nodes from a rosbag2; across the day/night lighting change only with local contrast equalization (daylight session: 33% of frames at 2.6 cm; dawn session: none) scripts/openloris_to_euroc.py, basalt_euroc_localize_demo --vio or --odometry (notes)
Robot camera sequence vs. a prebuilt map Sequential map-matching localization Benchmarked (simulation) RNE house (drjohnson): 387/400 frames at 5.6 cm ATE, 38 ms per frame (median) with --gpu --motion-model. RNE checker corridor: 203/400 at 1.57 m, limited by repetitive texture localize_rne_map_sequence
Camera + GNSS prior Tracking Example only Synthetic smoke test; no tight GNSS fusion track_sequence_with_gnss_prior
VO + GNSS positions Joint pose-graph fusion Experimental (synthetic) ~1 km synthetic loop: ATE 5.25 m (VO only) → 0.90 m fused; multipath jumps rejected, 15 s dropout bridged. Loosely coupled (receiver ENU positions; no raw pseudoranges, no IMU) gnss_vo_fusion_demo (notes)

LiDAR is not supported. Wheel odometry is supported only as the odometry that map fixes correct into a map's frame (above); it is not fused into the VIO.

Try it

Requires Rust 1.88+ only. Datasets are external inputs and are not bundled.

# Photos -> camera poses, Gaussian splat and mesh (GPU via wgpu)
cargo run --release -p visloc-gsplat-train --features gpu,euroc --example gsplat_photos --   --images /path/to/my_photos --out runs/my-scene

# Stereo-inertial VI-SLAM on a EuRoC sequence
cargo run --release --example basalt_euroc_online_slam_demo --features basalt-lm-workspace-reuse --   --euroc-dir /path/to/MH_01_easy   --calibration configs/basalt/variants/official_euroc_ds/euroc_ds_calib.json   --config configs/basalt/variants/official_euroc_ds/euroc_config.json   --out-dir target/basalt_mh01_online

# Smallest end-to-end check: localize a synthetic query, no data needed
cargo run --example localize_dummy

Full options for each are in the sections below; scripts/check.sh runs the complete local quality gate (fmt, clippy, test, doc).

Prebuilt binaries: each release attaches gsplat_photos, visloc_mvs, colmap_incremental_mapper and visloc_web for Linux x86_64, Windows x86_64 and macOS (Apple silicon), so the photo pipeline runs without a Rust toolchain.

Structure from Motion

visloc-rs registers 9,996/10,008 cameras (99.88%) across every ETH3D low-resolution many-view scene with no mapper run above 3.32 GiB, reconstructs the 1,200-image Electro set 3.46× faster than COLMAP with 25.2% lower camera-centre RMSE, and beats official COLMAP on the 38-image courtyard control (0.5379 cm vs 1.6166 cm).

Ten measured ETH3D reconstructions generated from 10,008 real images: camera-centre trajectories, registration, score-only RMSE, and bounded mapper memory

Real scene (ETH3D low-res many-view) Registered / supplied Centre RMSE RMSE / extent Mapper peak RSS
terrains 660/660 0.58 cm 0.12% 1.56 GiB
delivery area 948/948 9.22 cm 0.99% 2.31 GiB
forest 1028/1028 1.33 cm 0.19% 2.65 GiB
playground 955/960 6.12 cm 2.52% 2.44 GiB
electro 1200/1200 3.50 cm 0.55% 1.39 GiB
lakeside 1063/1064 0.34 cm 0.08% 3.19 GiB
sand box 1112/1112 2.35 cm 0.45% 3.32 GiB
storage room 795/796 0.61 cm 0.42% 1.71 GiB
storage room 2 831/832 3.48 cm 2.57% 1.00 GiB
tunnel 1404/1408 14.92 cm 1.61% 3.25 GiB

tunnel includes one 5.32 m outlier (median 2.47 cm, p95 9.41 cm); playground excludes five hash-audited source outliers. The connected 10k corridor stress run, the 300-image reliability gate, the OpenLORIS comparison, and all reproduction commands are in the SfM benchmark details.

Measured ETH3D Electro 1,200-image reconstruction: visloc-rs and COLMAP camera centres, sparse structure, residuals, mapper time, and peak memory

Same-input CPU8 Electro 1,200: visloc-rs 3.46× faster and 25.2% lower camera-centre RMSE than COLMAP. Unordered SfM, sequential SfM vs COLMAP, and EuRoC reconstruction evidence are in the SfM benchmark docs.

Against COLMAP 4.2 and GLOMAP on the same inputs

On the 13 ETH3D DSLR training scenes, the visloc-rs mapper is on par with COLMAP 4.2's incremental mapper and its global mapper (GLOMAP), and maps about 7× faster. All three get the same COLMAP SIFT features and exhaustive matches, the GLOMAP paper's setup, and the scoring follows that paper:

Mapper (pycolmap 4.2.1, 4 threads) Mean AUC@1° / 3° / 5° Registered Mapper time
visloc-rs 50.9 / 75.5 / 82.1 445 / 454 28.4 s
COLMAP incremental 50.4 / 75.2 / 81.7 445 / 454 200.7 s
COLMAP global (GLOMAP) 51.6 / 75.5 / 81.7 454 / 454 184.4 s

On visloc-rs's own matches (GPU SIFT, the default) it leads both at every threshold: 53.1 / 78.9 / 85.7. On the 5,822 SmallCity photos, visloc-rs maps in 21.7 min; COLMAP's global mapper had not finished after 94 min. Per-scene tables, timings and caveats: ETH3D AUC benchmark.

Large connected collections

On the connected OpenLORIS 10k stress set, streamed VLAD + LSH cuts candidate generation from 49:49 to 8:51 (5.63×); RMSE parity with COLMAP is still open (0.3890 m vs 0.3843 m). Full tables and caveats: SfM benchmark details.

GPU SfM vs COLMAP (CUDA) on EuRoC

Same 200 undistorted frames per sequence, same fixed intrinsics, same GPU, run back to back. visloc-rs uses GPU SIFT, batched GPU matching and the COLMAP incremental-mapper port. COLMAP 4.1 uses GPU extraction, GPU sequential matching and its default mapper. ATE is the Sim(3) camera-centre RMSE against ground truth.

Sequence Time (visloc-rs / COLMAP) ATE (visloc-rs / COLMAP) Registered (visloc-rs / COLMAP)
MH_01_easy 96 s / 634 s 0.35 / 0.35 cm 182 / 200
MH_03_medium 113 s / 340 s 1.32 / 2.61 cm 166 / 200
MH_05_difficult 102 s / 764 s 2.58 / 193.66 cm 194 / 200
V1_01_easy 104 s / 219 s 2.40 / 2.75 cm 199 / 200
V1_02_medium 55 s / 101 s 1.76 / 1.83 cm 198 / 200
V2_01_easy 81 s / 172 s 3.30 / 1.00 cm 199 / 200
V1_03_difficult 45 s / 93 s 2.17 / 1.98 cm 67 / 80
V2_03_difficult 42 s / 60 s 3.37 / 2.85 cm 118 / 180

visloc-rs is faster on all 8 sequences (1.4–7.5×). It is more accurate on 4 and equal on MH_01. COLMAP is more accurate on V2_01, V1_03 and V2_03, and it registers more frames, most visibly on the blurred V1_03 and V2_03.

COLMAP's results vary between runs: an earlier run gave V2_01 34.9 cm, and MH_05 failed in both runs. visloc-rs is deterministic.

EuRoC MH_05: COLMAP 4.1 GPU trajectory breaks (ATE 193.7 cm, 764 s) while visloc-rs stays on ground truth (2.58 cm, 102 s), then a 3D Gaussian Splatting flythrough trained from the visloc-rs poses

EuRoC MH_05, same 200 frames, same GPU. COLMAP breaks (ATE 193.7 cm, 764 s), visloc-rs stays at 2.58 cm in 102 s. The visloc-rs poses then train a 3D Gaussian Splatting scene: raw frames to splat in pure Rust + wgpu, no COLMAP.

Description taken from the repository README.

Ratings

–
0 ratings
5 0
4 0
3 0
2 0
1 0

Rate this app

Sign in to rate

Comments 0

Sign in to comment

No comments yet.

Related apps

#slam · #rust · #computer-vision