1use cumulus3d_core::{ImageId, Reconstruction};
32use cumulus3d_dense::{
33 undistort_with, DenseOutput, DenseScene, DensifyOptions, DepthMapCache, ImageBuffer, MvsProfile, PatchMatchBackend, SceneOptions,
34 UndistortBackend, UndistortCache, UndistortOptions,
35};
36use std::collections::BTreeMap;
37use std::path::{Path, PathBuf};
38use std::sync::{Arc, Mutex};
39use std::time::{Duration, Instant};
40
41pub fn make_pm_backend(name: &str) -> Result<Arc<dyn PatchMatchBackend>, String> {
43 match name {
44 "cuda" => cumulus3d_cuda::CudaPatchMatch::try_default()
45 .map(|b| Arc::new(b) as Arc<dyn PatchMatchBackend>)
46 .map_err(|e| format!("--pm-backend cuda: {e} (조밀화에는 CUDA 장치가 필요)")),
47 other => Err(format!("알 수 없는 --pm-backend {other} (cuda)")),
48 }
49}
50
51pub fn make_undistort_backend(name: &str) -> Result<Option<Arc<dyn UndistortBackend>>, String> {
53 match name {
54 "cpu" => Ok(None),
55 "cuda" => cumulus3d_cuda::CudaUndistort::try_default()
56 .map(|b| Some(Arc::new(b) as Arc<dyn UndistortBackend>))
57 .map_err(|e| format!("--undistort-backend cuda: {e}")),
58 other => Err(format!("알 수 없는 --undistort-backend {other} (cpu|cuda)")),
59 }
60}
61
62pub fn parse_profile(s: &str) -> Result<MvsProfile, String> {
64 MvsProfile::parse(s).ok_or_else(|| format!("알 수 없는 --mvs-profile {s} (fast|quality)"))
65}
66
67pub fn make_ba_backend(name: &str) -> Result<Arc<dyn cumulus3d_ba::BaBackend>, String> {
69 match name {
70 "cpu" => Ok(Arc::new(cumulus3d_ba::CpuBaBackend)),
71 "cuda" => cumulus3d_cuda::CudaBundleAdjuster::try_default()
72 .map(|b| Arc::new(b) as Arc<dyn cumulus3d_ba::BaBackend>)
73 .map_err(|e| format!("--ba-backend cuda: {e}")),
74 other => Err(format!("알 수 없는 --ba-backend {other} (cpu|cuda)")),
75 }
76}
77
78pub fn make_sift_backend(name: &str) -> Result<Arc<dyn cumulus3d_features::SiftEngine>, String> {
80 match name {
81 "cpu" => Ok(Arc::new(cumulus3d_features::CpuSift::default())),
82 "cuda" => cumulus3d_cuda::CudaSift::try_default()
83 .map(|b| Arc::new(b) as Arc<dyn cumulus3d_features::SiftEngine>)
84 .map_err(|e| format!("--sift-backend cuda: {e}")),
85 other => Err(format!("알 수 없는 --sift-backend {other} (cpu|cuda)")),
86 }
87}
88
89pub fn make_match_backend(name: &str) -> Result<Box<dyn cumulus3d_matching::MatcherBackend>, String> {
91 match name {
92 "cpu" => Ok(Box::new(cumulus3d_matching::CpuMatcher::default())),
93 "cuda" => cumulus3d_cuda::CudaMatcher::try_default()
94 .map(|b| Box::new(b) as Box<dyn cumulus3d_matching::MatcherBackend>)
95 .map_err(|e| format!("--match-backend cuda: {e}")),
96 other => Err(format!("알 수 없는 --match-backend {other} (cpu|cuda)")),
97 }
98}
99
100pub fn make_sparse_backend(name: &str) -> Result<crate::session::SparseBackends, String> {
103 match name {
104 "cpu" => Ok(crate::session::SparseBackends::default()),
105 "cuda" => cumulus3d_cuda::CudaSparse::try_default()
106 .map(|b| {
107 let b = Arc::new(b);
108 crate::session::SparseBackends { two_view: b.clone(), abs_pose: b.clone(), triangulation: b }
109 })
110 .map_err(|e| format!("--sparse-backend cuda: {e}")),
111 other => Err(format!("알 수 없는 --sparse-backend {other} (cpu|cuda)")),
112 }
113}
114
115#[derive(Clone)]
117pub struct DenseConfig {
118 pub undistort: UndistortOptions,
120 pub scene: SceneOptions,
122 pub densify: DensifyOptions,
124 pub score: Option<cumulus3d_dense::fusion_score::ScoreFusionOptions>,
126 pub backend: Result<Arc<dyn PatchMatchBackend>, String>,
128 pub lock: Option<Arc<Mutex<()>>>,
130 pub depth_cache: Option<Arc<DepthMapCache>>,
132 pub undistort_cache: Arc<UndistortCache>,
134 pub undistort_backend: Option<Arc<dyn UndistortBackend>>,
136}
137
138impl DenseConfig {
139 pub fn new(backend: Result<Arc<dyn PatchMatchBackend>, String>, profile: MvsProfile) -> Self {
141 Self {
142 undistort: UndistortOptions::pipeline(),
143 scene: SceneOptions::default(),
144 densify: DensifyOptions::with_profile(profile),
145 score: None,
146 backend,
147 lock: None,
148 depth_cache: None,
149 undistort_cache: Arc::new(UndistortCache::new()),
150 undistort_backend: None,
151 }
152 }
153}
154
155pub struct DenseRun {
157 pub frames: usize,
159 pub scene: DenseScene,
161 pub output: DenseOutput,
163 pub undistort_time: Duration,
165 pub densify_time: Duration,
167 pub lock_wait: Duration,
169}
170
171pub fn dense_model(model: &Reconstruction, keep: impl Fn(&str) -> bool, image_root: &Path, cfg: &DenseConfig) -> Result<DenseRun, String> {
174 let backend = cfg.backend.clone()?;
175 let t0 = Instant::now();
176 let mut m = model.clone();
177 let del: Vec<String> =
178 m.registered_images().into_iter().filter_map(|id| m.image(id).map(|im| im.name.clone())).filter(|n| !keep(n)).collect();
179 let del_ref: Vec<&str> = del.iter().map(|s| s.as_str()).collect();
180 m.deregister_images_by_name(&del_ref);
181 let root: PathBuf = image_root.to_path_buf();
182 let und = undistort_with(
183 &m,
184 &cfg.undistort,
185 &cfg.undistort_cache,
186 |im| ImageBuffer::load(root.join(&im.name)).ok().map(Arc::new),
187 cfg.undistort_backend.as_deref(),
188 )
189 .map_err(|e| format!("왜곡 보정 실패: {e}"))?;
190 let images: BTreeMap<ImageId, Arc<ImageBuffer>> = und.images;
191 let frames = images.len();
192 let scene = DenseScene::from_reconstruction(&und.reconstruction, &images, &cfg.scene).map_err(|e| format!("장면 변환 실패: {e}"))?;
193 drop(images);
194 let undistort_time = t0.elapsed();
195 let tw = Instant::now();
196 let guard = cfg.lock.as_ref().map(|l| l.lock().unwrap_or_else(|p| p.into_inner()));
197 let lock_wait = tw.elapsed();
198 let t1 = Instant::now();
199 let output =
200 cumulus3d_dense::densify::densify_with(&scene, &cfg.densify, cfg.score.as_ref(), backend.as_ref(), cfg.depth_cache.as_deref())
201 .map_err(|e| format!("조밀화 실패: {e}"));
202 drop(guard);
203 let output = output?;
204 Ok(DenseRun { frames, scene, output, undistort_time, densify_time: t1.elapsed(), lock_wait })
205}