colmap-rs/README.md

9.4 KiB

colmap (Rust)

A pure-Rust port of COLMAP / PyCOLMAP — the Structure-from-Motion (SfM) and Multi-View Stereo (MVS) data model, file I/O and geometry, with the same module structure and API names as PyCOLMAP.

No C++ build, no FFI, no system dependencies: it compiles with plain cargo. The numerical core relies only on [nalgebra].

Inspired by the existing (Chinese-documented) colmap crate, rebuilt from scratch against the upstream COLMAP C++ headers so the data model, file formats and camera models are byte-faithful.

Status

The library is laid out to mirror PyCOLMAP one-to-one. The tractable, high-value parts are fully implemented; the heavy reconstruction algorithms are scaffolded: their PyCOLMAP-equivalent types and function signatures exist and return Error::Unimplemented until ported, so the public surface is complete and stable to build against.

Module PyCOLMAP equivalent Status
types id aliases, SensorType, camera-model registry implemented
math Eigen vector/matrix vocabulary (nalgebra aliases) implemented
geometry Rigid3d, Sim3d, Rotation3d, essential/homography, triangulation, GPS implemented
sensor all 17 camera models + projection/undistortion implemented
image image size + EXIF, infer_camera_from_image implemented
scene Camera, Image, Point2D/3D, Track, Rig, Frame, Reconstruction, … implemented
io sparse-model .bin / .txt read & write implemented (byte-compatible)
database SQLite feature database behind the database feature
estimators RANSAC options, estimate_rigid3d/estimate_sim3d (Umeyama) partial · 🚧 RANSAC/PnP scaffolded
feature keypoints/descriptors/matches types, SIFT options types · 🚧 extraction/matching scaffolded
sfm incremental mapping & triangulation 🚧 scaffolded
mvs dense patch-match & fusion 🚧 scaffolded
optim bundle adjustment 🚧 scaffolded
retrieval vocabulary-tree image retrieval 🚧 scaffolded
pipeline high-level one-call pipelines 🚧 scaffolded
highlevel flat feature→SfM→MVS→export API (the other crate's shape) runs end-to-end (synthetic core)

The highlevel module is the odd one out: it offers the flat, ergonomic API of the higher-level colmap crate and runs the whole pipeline end to end, writing valid .ply / .obj / model files. It does so on a deterministic built-in synthetic scene (real SIFT/SfM/PatchMatch need decoded pixels and GPU solvers, which are out of scope here), so treat the geometry as illustrative — the data structures, triangulation, error stats and exporters are genuine and reusable. See the module docs for details.

Tested: 181 unit tests + 16 doc-tests, clippy-clean, docs build with -D warnings. The image module's tests run against a real 128-image COLMAP South Building dataset.

Quick start

use colmap::scene::Reconstruction;

// Read a sparse model directory (auto-detects binary vs text).
let rec = Reconstruction::read("/path/to/sparse/0".as_ref())?;
println!("{} cameras, {} images, {} points",
    rec.num_cameras(), rec.num_images(), rec.num_points3d());
println!("mean reprojection error: {:.3}px", rec.compute_mean_reprojection_error());
# Ok::<(), colmap::Error>(())

Infer a camera straight from an image header (size + EXIF), like PyCOLMAP's infer_camera_from_image:

use colmap::image::infer_camera_from_image;

let cam = infer_camera_from_image("photo.jpg".as_ref(), 1)?;
println!("{} {}x{} {:?}", cam.model_name(), cam.width, cam.height, cam.params);
# Ok::<(), colmap::Error>(())

Project a 3D point and compose transforms (b_from_a convention, identical to COLMAP):

use colmap::geometry::Rigid3d;
use colmap::math::{UnitQuat, Vec3};

let cam_from_world = Rigid3d::new(UnitQuat::identity(), Vec3::new(1.0, 2.0, 3.0));
let center = cam_from_world.target_origin_in_source(); // camera center in world coords
assert_eq!(center, Vec3::new(-1.0, -2.0, -3.0));

There is a runnable example that infers cameras for a whole folder:

cargo run --example infer_camera -- ../images

Pipeline examples

The same three examples as the reference colmap crate, with this crate's real names (the reconstruction steps are scaffolded, so the full workflow is no_run).

1. Basic building blocks (runs):

use colmap::scene::Camera;
use colmap::types::CameraModelId;
use colmap::math::{Vec2, Vec3};

let camera = Camera::new_with_model(1, CameraModelId::Pinhole, 800.0, 640, 480);
assert_eq!(camera.model_name(), "PINHOLE");
// An on-axis point projects onto the principal point.
let uv = camera.img_from_cam(&Vec3::new(0.0, 0.0, 1.0)).unwrap();
assert_eq!(uv, Vec2::new(320.0, 240.0));

2. Complete reconstruction workflow (flat highlevel API, runs end to end):

use colmap::*;
use std::path::Path;

fn reconstruct_from_images(image_dir: &Path) -> Result<()> {
    // 1. Load images.
    let images = load_images_from_directory(image_dir)?;

    // 2. Feature extraction and matching.
    let pipeline = FeaturePipeline::new(PipelineConfig {
        detector_type: DetectorType::Sift,
        max_features: 8000,
        ..Default::default()
    });
    let extraction = pipeline.extract_and_match_all(&images)?;

    // 3. Sparse SfM reconstruction.
    let mut sfm = IncrementalSfm::new(SfmConfig { min_track_length: 2, ..Default::default() });
    sfm.set_features(extraction.features);
    sfm.set_matches(extraction.matches);
    let sparse = sfm.reconstruct()?;
    println!("{} images, {} points, {:.2}px error",
        sparse.registered_images(), sparse.points.len(), sparse.mean_reprojection_error());

    // 4. Dense MVS reconstruction.
    let mvs = MvsReconstructor::new(MvsConfig { min_num_views: 3, ..Default::default() });
    let views = prepare_views_from_reconstruction(&sparse)?;
    let dense = mvs.reconstruct(&views)?;

    // 5. Save results: COLMAP model dir, dense point cloud (.ply), mesh (.obj).
    save_reconstruction(&sparse, "sparse_reconstruction")?;
    save_point_cloud(&dense.point_cloud, "dense_point_cloud.ply")?;
    save_mesh(&dense.mesh, "mesh.obj")?;
    Ok(())
}

3. Error handling (runs):

use colmap::{Error, Result};
use colmap::mvs::{patch_match_stereo, PatchMatchOptions};
use std::path::Path;

let result: Result<()> = patch_match_stereo(Path::new("/tmp/ws"), &PatchMatchOptions::default());
match result {
    Ok(()) => println!("dense reconstruction done"),
    Err(Error::Unimplemented(what)) => eprintln!("step not ported yet: {what}"),
    Err(Error::Io(err)) => eprintln!("I/O error: {err}"),
    Err(err) => eprintln!("other error: {err}"),
}

Example 2 is runnable as examples/full_pipeline.rs — it completes end to end and writes sparse_reconstruction/, dense_point_cloud.ply and mesh.obj (the highlevel core runs on a deterministic synthetic scene; the data structures, triangulation, error stats and the PLY/OBJ/model writers are genuine):

cargo run --example full_pipeline -- /path/to/images

The PyCOLMAP-style stage API (extract_features, incremental_mapping, patch_match_stereo, …) is shown in examples/reconstruct.rs, where the not-yet-ported stages report Error::Unimplemented:

cargo run --example reconstruct -- ../images

Camera models

All 17 COLMAP camera models are implemented with their exact parameter ordering, projection (img_from_cam) and unprojection (cam_from_img, iterative where needed): SIMPLE_PINHOLE, PINHOLE, SIMPLE_RADIAL, RADIAL, OPENCV, OPENCV_FISHEYE, FULL_OPENCV, FOV, SIMPLE_RADIAL_FISHEYE, RADIAL_FISHEYE, THIN_PRISM_FISHEYE, RAD_TAN_THIN_PRISM_FISHEYE, SIMPLE_DIVISION, DIVISION, SIMPLE_FISHEYE, FISHEYE, EUCM.

Cargo features

Feature Default Description
database off SQLite feature database via bundled rusqlite (no system SQLite required).
serde off Serialize/Deserialize derives for the public data types.
[dependencies]
colmap = { version = "0.1", features = ["database", "serde"] }

File-format compatibility

io reads and writes COLMAP's sparse model files byte-compatibly:

  • Binary: cameras.bin, images.bin, points3D.bin (little-endian, exact field widths).
  • Text: cameras.txt, images.txt, points3D.txt.

Reconstruction::read/write auto-detect the format and round-trip is covered by tests.

Roadmap

The scaffolded modules are where contributions land next, roughly in order of leverage:

  1. SIFT feature extraction & matching (feature).
  2. RANSAC estimators: essential/fundamental/homography, PnP absolute pose (estimators).
  3. Incremental mapping (sfm) and bundle adjustment (optim).
  4. Dense MVS: patch-match stereo & fusion (mvs).

License

BSD-3-Clause, matching upstream COLMAP.