From dcd7e4662b7a822795a54c06f5652d0dfb788487 Mon Sep 17 00:00:00 2001 From: Dmitry Zolotukhin Date: Sat, 25 May 2024 19:37:11 +0200 Subject: [PATCH] Added option to limit number of points. This helps to have a sane mesh size without losing data. --- README.md | 6 +++++- src/main.rs | 8 ++++++++ src/reconstruction.rs | 5 +++-- src/triangulation.rs | 15 +++++++++++++-- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index dc9035f..59dab94 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Download a release distribution from [releases](/zlogic/cybervision/releases). Run cybervision: ```shell -cybervision [--scale=] [--focal-length=] [--mode=] [--interpolation=] [--projection=] [--mesh=] [--no-bundle-adjustment] [] +cybervision [--scale=] [--focal-length=] [--mode=] [--interpolation=] [--projection=] [--mesh=] [--no-bundle-adjustment] [--max-points=] [] ``` `--scale=` is an optional argument to specify a depth scale, for example `--scale=-10.0`. @@ -50,6 +50,10 @@ If not specified, EXIF metadata will be used. `--no-bundle-adjustment` disables bundle adjustment when reconstructing images with perspective projection. Adding this flag can significantly reduce processing time, at the cost of producing incorrect data. +`--max-points=` sets a limit on the number of points in the resulting mesh +Adding this flag will randomly select up to max\_points points if the mesh contains too many points. +This helps with producing reasonably sized meshes (dense reconstruction produces a lot of data!). + `` and `` are input filenames for image 1 and 2; supported formats are `jpg`, `tif` and `png`. Although experimental, it's also possible to specify more than one image when using perspective projection. diff --git a/src/main.rs b/src/main.rs index 9ad5db7..21e6d05 100644 --- a/src/main.rs +++ b/src/main.rs @@ -42,6 +42,7 @@ pub struct Args { mode: HardwareMode, interpolation: InterpolationMode, no_bundle_adjustment: bool, + max_points: Option, projection: ProjectionMode, mesh: Mesh, img_src: Vec, @@ -58,6 +59,7 @@ Options:\ \n --mode= Hardware mode [default: gpu] [possible values: gpu, gpu-low-power, cpu]\ \n --interpolation= Interpolation mode [default: delaunay] [possible values: delaunay, none]\ \n --no-bundle-adjustment Skip bundle adjustment [if unspecified, bundle adjustment will be applied]\ +\n --max-points= Limit number of points in the resulting mesh\ \n --projection= Projection mode [default: perspective] [possible values: parallel, perspective]\ \n --mesh= Mesh options [default: vertex-colors] [possible values: plain, vertex-colors, texture-coordinates]\ \n --help Print help"; @@ -69,6 +71,7 @@ impl Args { mode: HardwareMode::Gpu, interpolation: InterpolationMode::Delaunay, no_bundle_adjustment: false, + max_points: None, projection: ProjectionMode::Perspective, mesh: Mesh::VertexColors, img_src: vec![], @@ -132,6 +135,11 @@ impl Args { exit(2); } }; + } else if name == "--max-points" { + match value.parse() { + Ok(max_points) => args.max_points = Some(max_points), + Err(err) => fail_with_error(name, value, &err), + }; } else if name == "--projection" { match value { "perspective" => args.projection = ProjectionMode::Perspective, diff --git a/src/reconstruction.rs b/src/reconstruction.rs index d46ac98..9562867 100644 --- a/src/reconstruction.rs +++ b/src/reconstruction.rs @@ -305,7 +305,7 @@ pub fn reconstruct(args: &Args) -> Result<(), ReconstructionError> { } }; - let surface = reconstruction_task.complete_triangulation(linked_images)?; + let surface = reconstruction_task.complete_triangulation(linked_images, args.max_points)?; let img_filenames = reconstruction_task.img_filenames.to_owned(); reconstruction_task.output_surface( surface, @@ -732,12 +732,13 @@ impl ImageReconstruction { fn complete_triangulation( &mut self, linked_images: Vec, + max_points: Option, ) -> Result { let start_time = SystemTime::now(); let pb = new_progress_bar(false); - let surface = self.triangulation.triangulate_all(Some(&pb))?; + let surface = self.triangulation.triangulate_all(max_points, Some(&pb))?; self.triangulation.complete(); pb.finish_and_clear(); diff --git a/src/triangulation.rs b/src/triangulation.rs index 0cfdcbb..4f20d04 100644 --- a/src/triangulation.rs +++ b/src/triangulation.rs @@ -6,7 +6,7 @@ use nalgebra::{ Vector2, Vector3, Vector4, }; -use rand::{rngs::SmallRng, Rng, SeedableRng}; +use rand::{rngs::SmallRng, seq::SliceRandom, Rng, SeedableRng}; use rayon::prelude::*; const BUNDLE_ADJUSTMENT_MAX_ITERATIONS: usize = 100; @@ -225,12 +225,13 @@ impl Triangulation { pub fn triangulate_all( &mut self, + max_points: Option, progress_listener: Option<&PL>, ) -> Result { if let Some(affine) = &self.affine { affine.triangulate_all() } else if let Some(perspective) = &mut self.perspective { - perspective.triangulate_all(progress_listener) + perspective.triangulate_all(max_points, progress_listener) } else { Err("Triangulation not initialized".into()) } @@ -705,6 +706,7 @@ impl PerspectiveTriangulation { fn triangulate_all( &mut self, + max_points: Option, progress_listener: Option<&PL>, ) -> Result { self.triangulate_tracks(); @@ -722,6 +724,15 @@ impl PerspectiveTriangulation { self.bundle_adjustment(cameras.as_slice(), progress_listener)?; } + if let Some(max_points) = max_points { + let rng = &mut SmallRng::from_rng(rand::thread_rng()).unwrap(); + if self.tracks.len() > max_points { + self.tracks.shuffle(rng); + self.tracks.truncate(max_points); + self.tracks.shrink_to_fit(); + } + }; + let surface_projections = cameras .iter() .map(|camera| camera.projection())