Initial commit: pure-Rust COLMAP port
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
//! Full feature → SfM → MVS → export pipeline, using the flat high-level API.
|
||||
//!
|
||||
//! This is the example from the high-level `colmap` crate, running verbatim
|
||||
//! against this crate. The numerical core is the built-in synthetic-scene demo
|
||||
//! (see `colmap::highlevel`), so it runs end to end and writes real output files
|
||||
//! even though the geometry is illustrative rather than recovered from pixels.
|
||||
//!
|
||||
//! ```text
|
||||
//! cargo run --example full_pipeline -- /path/to/images
|
||||
//! ```
|
||||
|
||||
use colmap::*;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
fn reconstruct_from_images(image_dir: &Path) -> Result<()> {
|
||||
// 1. Load images (headers only).
|
||||
let images = load_images_from_directory(image_dir)?;
|
||||
|
||||
// 2. Feature extraction and matching.
|
||||
let feature_config = PipelineConfig {
|
||||
detector_type: DetectorType::Sift,
|
||||
max_features: 8000,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let pipeline = FeaturePipeline::new(feature_config);
|
||||
let extraction_result = pipeline.extract_and_match_all(&images)?;
|
||||
|
||||
println!("Extracted features for {} images", extraction_result.features.len());
|
||||
println!("Found {} match pairs", extraction_result.matches.len());
|
||||
|
||||
// 3. Sparse SfM reconstruction.
|
||||
let sfm_config = SfmConfig {
|
||||
min_track_length: 2,
|
||||
max_reprojection_error: 4.0,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut sfm_reconstructor = IncrementalSfm::new(sfm_config);
|
||||
sfm_reconstructor.set_features(extraction_result.features);
|
||||
sfm_reconstructor.set_matches(extraction_result.matches);
|
||||
|
||||
let sparse_reconstruction = sfm_reconstructor.reconstruct()?;
|
||||
|
||||
println!("Sparse reconstruction:");
|
||||
println!(" - registered images: {}", sparse_reconstruction.registered_images());
|
||||
println!(" - 3D points: {}", sparse_reconstruction.points.len());
|
||||
println!(
|
||||
" - mean reprojection error: {:.2}",
|
||||
sparse_reconstruction.mean_reprojection_error()
|
||||
);
|
||||
|
||||
// 4. Dense MVS reconstruction.
|
||||
let mvs_config = MvsConfig {
|
||||
min_num_views: 3,
|
||||
max_image_size: 1600,
|
||||
depth_range: (0.1, 100.0),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mvs_reconstructor = MvsReconstructor::new(mvs_config);
|
||||
let views = prepare_views_from_reconstruction(&sparse_reconstruction)?;
|
||||
let dense_reconstruction = mvs_reconstructor.reconstruct(&views)?;
|
||||
|
||||
println!("Dense reconstruction:");
|
||||
println!(" - point cloud size: {}", dense_reconstruction.point_cloud.points.len());
|
||||
println!(" - mesh triangles: {}", dense_reconstruction.mesh.triangles.len());
|
||||
|
||||
// 5. Save the results.
|
||||
save_reconstruction(&sparse_reconstruction, "sparse_reconstruction")?;
|
||||
save_point_cloud(&dense_reconstruction.point_cloud, "dense_point_cloud.ply")?;
|
||||
save_mesh(&dense_reconstruction.mesh, "mesh.obj")?;
|
||||
|
||||
println!("Wrote sparse_reconstruction/, dense_point_cloud.ply, mesh.obj");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let image_dir: PathBuf = std::env::args()
|
||||
.nth(1)
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|| PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/../images")));
|
||||
|
||||
println!("Reconstructing from {}", image_dir.display());
|
||||
if let Err(err) = reconstruct_from_images(&image_dir) {
|
||||
eprintln!("error: {err}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
//! Infer COLMAP cameras from a folder of images using only their headers (size + EXIF).
|
||||
//!
|
||||
//! Run with:
|
||||
//! ```text
|
||||
//! cargo run --example infer_camera -- ../images
|
||||
//! ```
|
||||
//! If no path is given it defaults to `../images` (the South Building sample set).
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use colmap::image::{infer_shared_cameras_in_dir, read_exif, read_image_size};
|
||||
|
||||
fn main() -> colmap::Result<()> {
|
||||
let dir: PathBuf = std::env::args()
|
||||
.nth(1)
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|| PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/../images")));
|
||||
|
||||
println!("Scanning {}", dir.display());
|
||||
|
||||
let images = colmap::image::list_images_in_dir(&dir)?;
|
||||
println!("Found {} images\n", images.len());
|
||||
|
||||
if let Some(first) = images.first() {
|
||||
let (w, h) = read_image_size(first)?;
|
||||
println!("First image: {}", first.file_name().unwrap().to_string_lossy());
|
||||
println!(" size: {w} x {h}");
|
||||
if let Some(exif) = read_exif(first)? {
|
||||
println!(" make/model: {:?} / {:?}", exif.make, exif.model);
|
||||
println!(" focal length: {:?} mm", exif.focal_length_mm);
|
||||
println!(
|
||||
" focal-plane res: {:?} (unit {:?})",
|
||||
exif.focal_plane_x_resolution, exif.focal_plane_resolution_unit
|
||||
);
|
||||
}
|
||||
let cam = colmap::image::infer_camera_from_image(first, 1)?;
|
||||
println!(
|
||||
" inferred camera: {} {}x{} params={:?} prior_focal={}",
|
||||
cam.model_name(),
|
||||
cam.width,
|
||||
cam.height,
|
||||
cam.params,
|
||||
cam.has_prior_focal_length
|
||||
);
|
||||
}
|
||||
|
||||
let (cameras, assignment) = infer_shared_cameras_in_dir(&dir)?;
|
||||
println!(
|
||||
"\nDeduplicated to {} camera(s) across {} images.",
|
||||
cameras.len(),
|
||||
assignment.len()
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
//! End-to-end reconstruction pipeline skeleton.
|
||||
//!
|
||||
//! This mirrors the full-program example of the reference `colmap` crate, using
|
||||
//! this crate's actual API. The implemented step (camera inference) runs for
|
||||
//! real; the scaffolded steps report that they are not ported yet instead of
|
||||
//! aborting, so you can see the intended flow end to end.
|
||||
//!
|
||||
//! ```text
|
||||
//! cargo run --example reconstruct -- ../images
|
||||
//! ```
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use colmap::feature::{FeatureExtractionOptions, FeatureMatchingOptions};
|
||||
use colmap::mvs::{patch_match_stereo, stereo_fusion, PatchMatchOptions, StereoFusionOptions};
|
||||
use colmap::sfm::{incremental_mapping, IncrementalPipelineOptions};
|
||||
|
||||
fn report(step: &str, result: colmap::Result<()>) {
|
||||
match result {
|
||||
Ok(()) => println!(" ✓ {step}"),
|
||||
Err(err) => println!(" … {step}: {err}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let image_dir: PathBuf = std::env::args()
|
||||
.nth(1)
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|| PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/../images")));
|
||||
let database = Path::new("/tmp/colmap-rs/database.db");
|
||||
let workspace = Path::new("/tmp/colmap-rs/sparse");
|
||||
|
||||
println!("Reconstructing from {}", image_dir.display());
|
||||
|
||||
// 1. Camera inference from image headers (implemented).
|
||||
match colmap::image::infer_shared_cameras_in_dir(&image_dir) {
|
||||
Ok((cameras, assignment)) => println!(
|
||||
" ✓ inferred {} camera(s) for {} images",
|
||||
cameras.len(),
|
||||
assignment.len()
|
||||
),
|
||||
Err(err) => {
|
||||
eprintln!(" ✗ camera inference failed: {err}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Feature extraction & matching (scaffolded).
|
||||
report(
|
||||
"feature extraction",
|
||||
colmap::feature::extract_features(database, &image_dir, &FeatureExtractionOptions::default()),
|
||||
);
|
||||
report(
|
||||
"exhaustive matching",
|
||||
colmap::feature::match_exhaustive(database, &FeatureMatchingOptions::default()),
|
||||
);
|
||||
|
||||
// 3. Sparse SfM reconstruction (scaffolded).
|
||||
match incremental_mapping(database, &image_dir, workspace, &IncrementalPipelineOptions::default()) {
|
||||
Ok(recs) => println!(" ✓ sparse reconstruction: {} model(s)", recs.len()),
|
||||
Err(err) => println!(" … sparse SfM: {err}"),
|
||||
}
|
||||
|
||||
// 4. Dense MVS reconstruction (scaffolded).
|
||||
report("patch-match stereo", patch_match_stereo(workspace, &PatchMatchOptions::default()));
|
||||
report("stereo fusion", stereo_fusion(workspace, &StereoFusionOptions::default()));
|
||||
}
|
||||
Reference in New Issue
Block a user