# colmap (Rust) A **pure-Rust port of [COLMAP](https://colmap.github.io/) / [PyCOLMAP](https://colmap.github.io/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`](https://docs.rs/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`](https://docs.rs/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 ```rust,no_run 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`: ```rust,no_run 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): ```rust 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: ```sh 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): ```rust 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): ```rust,no_run 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): ```rust 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`](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): ```sh 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`](examples/reconstruct.rs), where the not-yet-ported stages report `Error::Unimplemented`: ```sh 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. | ```toml [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.