ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
automatic_reconstruction.cc
Go to the documentation of this file.
1 // Copyright (c) 2018, ETH Zurich and UNC Chapel Hill.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 //
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 //
14 // * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of
15 // its contributors may be used to endorse or promote products derived
16 // from this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 // POSSIBILITY OF SUCH DAMAGE.
29 //
30 // Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de)
31 
33 
34 #include "base/undistortion.h"
37 #include "feature/extraction.h"
38 #include "feature/matching.h"
39 #include "mvs/fusion.h"
40 #include "mvs/meshing.h"
41 #include "mvs/patch_match.h"
42 #include "util/download.h"
43 #include "util/misc.h"
44 #include "util/option_manager.h"
45 
46 namespace colmap {
47 
49  const Options& options, ReconstructionManager* reconstruction_manager)
50  : options_(options),
51  reconstruction_manager_(reconstruction_manager),
52  active_thread_(nullptr) {
55  CHECK_NOTNULL(reconstruction_manager_);
56 
58 
61  JoinPaths(options_.workspace_path, "database.db");
62 
65  } else if (options_.data_type == DataType::INDIVIDUAL) {
67  } else if (options_.data_type == DataType::INTERNET) {
69  } else {
70  LOG(FATAL) << "Data type not supported";
71  }
72 
74 
75  if (options_.quality == Quality::LOW) {
77  } else if (options_.quality == Quality::MEDIUM) {
79  } else if (options_.quality == Quality::HIGH) {
81  } else if (options_.quality == Quality::EXTREME) {
83  }
84 
89 
92  reader_options.image_path = *option_manager_.image_path;
93  if (!options_.mask_path.empty()) {
94  reader_options.mask_path = options_.mask_path;
96  }
97  reader_options.single_camera = options_.single_camera;
98  reader_options.camera_model = options_.camera_model;
99 
102  option_manager_.mapper->ba_use_gpu = options_.use_gpu;
104 
108  option_manager_.mapper->ba_gpu_index = options_.gpu_index;
110 
111  feature_extractor_.reset(new SiftFeatureExtractor(
112  reader_options, *option_manager_.sift_extraction));
113 
114  exhaustive_matcher_.reset(new ExhaustiveFeatureMatcher(
117 
118  // Resolve vocab_tree_path: use default if empty, and download/cache if URI
119  std::string resolved_vocab_tree_path = options_.vocab_tree_path;
120  if (resolved_vocab_tree_path.empty()) {
121  resolved_vocab_tree_path = retrieval::kDefaultVocabTreeUri;
122  }
123 
124  // Automatically download and cache if URI format is provided
125  if (!resolved_vocab_tree_path.empty()) {
126 #ifdef COLMAP_DOWNLOAD_ENABLED
127  // Download and cache the file if it's a URI, otherwise use as local path
128  resolved_vocab_tree_path = MaybeDownloadAndCacheFile(resolved_vocab_tree_path).string();
129  option_manager_.sequential_matching->loop_detection = true;
130  option_manager_.sequential_matching->vocab_tree_path = resolved_vocab_tree_path;
131 #else
132  // If download is disabled, check if it's a URI and warn, otherwise use as local path
133  if (resolved_vocab_tree_path.find("http://") == 0 ||
134  resolved_vocab_tree_path.find("https://") == 0 ||
135  resolved_vocab_tree_path.find(';') != std::string::npos) {
136  LOG(WARNING) << "vocab_tree_path appears to be a URI but download support is disabled. "
137  << "Please provide a local path or enable DOWNLOAD_ENABLED.";
138  resolved_vocab_tree_path = ""; // Clear to avoid using invalid URI
139  } else {
140  // Use as local path
141  option_manager_.sequential_matching->loop_detection = true;
142  option_manager_.sequential_matching->vocab_tree_path = resolved_vocab_tree_path;
143  }
144 #endif
145  }
146 
147  sequential_matcher_.reset(new SequentialFeatureMatcher(
150 
151  if (!resolved_vocab_tree_path.empty()) {
152  option_manager_.vocab_tree_matching->vocab_tree_path = resolved_vocab_tree_path;
153  vocab_tree_matcher_.reset(new VocabTreeFeatureMatcher(
156  }
157 }
158 
160  if (active_thread_ != nullptr) {
161  active_thread_->Stop();
162  }
163  Thread::Stop();
164 }
165 
166 void AutomaticReconstructionController::Run() {
167  if (IsStopped()) {
168  return;
169  }
170 
171  RunFeatureExtraction();
172 
173  if (IsStopped()) {
174  return;
175  }
176 
177  RunFeatureMatching();
178 
179  if (IsStopped()) {
180  return;
181  }
182 
183  if (options_.sparse) {
184  RunSparseMapper();
185  }
186 
187  if (IsStopped()) {
188  return;
189  }
190 
191  if (options_.dense) {
192  RunDenseMapper();
193  }
194 
196 }
197 
198 void AutomaticReconstructionController::RunFeatureExtraction() {
199  CHECK(feature_extractor_);
200  active_thread_ = feature_extractor_.get();
201  feature_extractor_->Start();
202  feature_extractor_->Wait();
203  feature_extractor_.reset();
204  active_thread_ = nullptr;
205 }
206 
207 void AutomaticReconstructionController::RunFeatureMatching() {
208  Thread* matcher = nullptr;
210  matcher = sequential_matcher_.get();
211  } else if (options_.data_type == DataType::INDIVIDUAL ||
213  Database database(*option_manager_.database_path);
214  const size_t num_images = database.NumImages();
215  // Use vocab tree matcher if it was created (vocab_tree_path was resolved) and num_images >= 200
216  if (vocab_tree_matcher_ && num_images >= 200) {
217  matcher = vocab_tree_matcher_.get();
218  } else {
219  matcher = exhaustive_matcher_.get();
220  }
221  }
222 
223  CHECK(matcher);
224  active_thread_ = matcher;
225  matcher->Start();
226  matcher->Wait();
227  exhaustive_matcher_.reset();
228  sequential_matcher_.reset();
229  vocab_tree_matcher_.reset();
230  active_thread_ = nullptr;
231 }
232 
233 void AutomaticReconstructionController::RunSparseMapper() {
234  const auto sparse_path = JoinPaths(options_.workspace_path, "sparse");
235  if (ExistsDir(sparse_path)) {
236  auto dir_list = GetDirList(sparse_path);
237  std::sort(dir_list.begin(), dir_list.end());
238  if (dir_list.size() > 0) {
239  std::cout << std::endl
240  << "WARNING: Skipping sparse reconstruction because it is "
241  "already computed"
242  << std::endl;
243  for (const auto& dir : dir_list) {
245  }
246  return;
247  }
248  }
249 
253  active_thread_ = &mapper;
254  mapper.Start();
255  mapper.Wait();
256  active_thread_ = nullptr;
257 
258  CreateDirIfNotExists(sparse_path);
260 }
261 
264 
265  for (size_t i = 0; i < reconstruction_manager_->Size(); ++i) {
266  if (IsStopped()) {
267  return;
268  }
269 
270  const std::string dense_path =
272  const std::string fused_path = JoinPaths(dense_path, "fused.ply");
273 
274  std::string meshing_path;
276  meshing_path = JoinPaths(dense_path, "meshed-poisson.ply");
277  } else if (options_.mesher == Mesher::DELAUNAY) {
278  meshing_path = JoinPaths(dense_path, "meshed-delaunay.ply");
279  }
280 
281  if (ExistsFile(fused_path) && ExistsFile(meshing_path)) {
282  continue;
283  }
284 
285  // Image undistortion.
286 
287  if (!ExistsDir(dense_path)) {
288  CreateDirIfNotExists(dense_path);
289 
290  UndistortCameraOptions undistortion_options;
291  undistortion_options.max_image_size =
292  option_manager_.patch_match_stereo->max_image_size;
293  COLMAPUndistorter undistorter(undistortion_options,
295  *option_manager_.image_path, dense_path);
296  active_thread_ = &undistorter;
297  undistorter.Start();
298  undistorter.Wait();
299  active_thread_ = nullptr;
300  }
301 
302  if (IsStopped()) {
303  return;
304  }
305 
306  // Patch match stereo.
307 
308 #ifdef CUDA_ENABLED
309  {
310  mvs::PatchMatchController patch_match_controller(
311  *option_manager_.patch_match_stereo, dense_path, "COLMAP", "");
312  active_thread_ = &patch_match_controller;
313  patch_match_controller.Start();
314  patch_match_controller.Wait();
315  active_thread_ = nullptr;
316  }
317 #else // CUDA_ENABLED
318  std::cout
319  << std::endl
320  << "WARNING: Skipping patch match stereo because CUDA is not available."
321  << std::endl;
322  return;
323 #endif // CUDA_ENABLED
324 
325  if (IsStopped()) {
326  return;
327  }
328 
329  // Stereo fusion.
330 
331  if (!ExistsFile(fused_path)) {
332  auto fusion_options = *option_manager_.stereo_fusion;
333  const int num_reg_images = reconstruction_manager_->Get(i).NumRegImages();
334  fusion_options.min_num_pixels =
335  std::min(num_reg_images + 1, fusion_options.min_num_pixels);
336  mvs::StereoFusion fuser(
337  fusion_options, dense_path, "COLMAP", "",
338  options_.quality == Quality::HIGH ? "geometric" : "photometric");
339  active_thread_ = &fuser;
340  fuser.Start();
341  fuser.Wait();
342  active_thread_ = nullptr;
343 
344  std::cout << "Writing output: " << fused_path << std::endl;
345  const auto fused_points = fuser.GetFusedPoints();
346  WriteBinaryPlyPoints(fused_path, fused_points);
347  mvs::WritePointsVisibility(fused_path + ".vis",
348  fuser.GetFusedPointsVisibility());
349 
350  // Hook for derived classes
351  OnFusedPointsGenerated(i, fused_points);
352  }
353 
354  if (IsStopped()) {
355  return;
356  }
357 
358  // Surface meshing.
359 
360  if (!ExistsFile(meshing_path)) {
363  meshing_path);
364  } else if (options_.mesher == Mesher::DELAUNAY) {
365 #ifdef CGAL_ENABLED
366  mvs::DenseDelaunayMeshing(*option_manager_.delaunay_meshing, dense_path,
367  meshing_path);
368 #else // CGAL_ENABLED
369  std::cout << std::endl
370  << "WARNING: Skipping Delaunay meshing because CGAL is "
371  "not available."
372  << std::endl;
373  return;
374 
375 #endif // CGAL_ENABLED
376  }
377 
378  // Hook for derived classes
379  if (ExistsFile(meshing_path)) {
380  OnMeshGenerated(i, meshing_path);
381  }
382  } else {
383  // Mesh already exists, notify derived classes
384  OnMeshGenerated(i, meshing_path);
385  }
386 
387  if (IsStopped()) {
388  return;
389  }
390 
391  // Surface texturing.
392  if (options_.texturing) {
393  const std::string textured_path =
394  JoinPaths(dense_path, "textured-mesh.obj");
395  if (!ExistsFile(textured_path) && ExistsFile(meshing_path)) {
396  option_manager_.texturing->meshed_file_path = meshing_path;
397  option_manager_.texturing->textured_file_path = textured_path;
398 
399  // Set mesh_source based on which mesher was used
401  option_manager_.texturing->mesh_source = "poisson";
402  } else if (options_.mesher == Mesher::DELAUNAY) {
403  option_manager_.texturing->mesh_source = "delaunay";
404  }
405 
406  TexturingReconstruction texturing(
409  *option_manager_.image_path, dense_path);
410  active_thread_ = &texturing;
411  texturing.Start();
412  texturing.Wait();
413  active_thread_ = nullptr;
414 
415  if (ExistsFile(textured_path)) {
416  std::cout << "Writing textured mesh: " << textured_path
417  << std::endl;
418  // Hook for derived classes
419  OnTexturedMeshGenerated(i, textured_path);
420  }
421  } else if (ExistsFile(textured_path)) {
422  // Textured mesh already exists, notify derived classes
423  OnTexturedMeshGenerated(i, textured_path);
424  }
425  }
426  }
427 }
428 
429 } // namespace colmap
virtual void OnTexturedMeshGenerated(size_t reconstruction_idx, const std::string &textured_path)
virtual void OnMeshGenerated(size_t reconstruction_idx, const std::string &mesh_path)
AutomaticReconstructionController(const Options &options, ReconstructionManager *reconstruction_manager)
virtual void OnFusedPointsGenerated(size_t reconstruction_idx, const std::vector< PlyPoint > &points)
std::shared_ptr< SequentialMatchingOptions > sequential_matching
std::shared_ptr< mvs::PoissonMeshingOptions > poisson_meshing
std::shared_ptr< mvs::DelaunayMeshingOptions > delaunay_meshing
std::shared_ptr< TexturingOptions > texturing
std::shared_ptr< mvs::PatchMatchOptions > patch_match_stereo
std::shared_ptr< std::string > database_path
std::shared_ptr< mvs::StereoFusionOptions > stereo_fusion
std::shared_ptr< SiftMatchingOptions > sift_matching
std::shared_ptr< BundleAdjustmentOptions > bundle_adjustment
std::shared_ptr< IncrementalMapperOptions > mapper
std::shared_ptr< SiftExtractionOptions > sift_extraction
std::shared_ptr< ImageReaderOptions > image_reader
std::shared_ptr< VocabTreeMatchingOptions > vocab_tree_matching
std::shared_ptr< ExhaustiveMatchingOptions > exhaustive_matching
std::shared_ptr< std::string > image_path
const Reconstruction & Get(const size_t idx) const
size_t Read(const std::string &path)
void Write(const std::string &path, const OptionManager *options) const
size_t NumRegImages() const
virtual void Start()
Definition: threading.cc:50
virtual void Stop()
Definition: threading.cc:65
const Timer & GetTimer() const
Definition: threading.cc:154
bool IsStopped()
Definition: threading.cc:97
virtual void Wait()
Definition: threading.cc:86
void PrintMinutes() const
Definition: timer.cc:93
const std::vector< std::vector< int > > & GetFusedPointsVisibility() const
Definition: fusion.cc:142
const std::vector< PlyPoint > & GetFusedPoints() const
Definition: fusion.cc:138
QTextStream & endl(QTextStream &stream)
Definition: QtCompat.h:718
colmap::IncrementalMapperController IncrementalMapperController
void WritePointsVisibility(const std::string &path, const std::vector< std::vector< int >> &points_visibility)
Definition: fusion.cc:635
bool PoissonMeshing(const PoissonMeshingOptions &options, const std::string &input_path, const std::string &output_path)
Definition: meshing.cc:123
static const std::string kDefaultVocabTreeUri
Definition: resources.h:25
bool ExistsCameraModelWithName(const std::string &model_name)
void WriteBinaryPlyPoints(const std::string &path, const std::vector< PlyPoint > &points, const bool write_normal, const bool write_rgb)
Definition: ply.cc:369
bool ExistsDir(const std::string &path)
Definition: misc.cc:104
bool ExistsFile(const std::string &path)
Definition: misc.cc:100
void CreateDirIfNotExists(const std::string &path)
Definition: misc.cc:112
std::string JoinPaths(T const &... paths)
Definition: misc.h:128
std::vector< std::string > GetDirList(const std::string &path)
Definition: misc.cc:195
std::filesystem::path MaybeDownloadAndCacheFile(const std::string &uri)
std::string to_string(const T &n)
Definition: Common.h:20