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 
32 #include "controllers/automatic_reconstruction.h"
33 
34 #include "base/undistortion.h"
35 #include "controllers/incremental_mapper.h"
36 #include "feature/extraction.h"
37 #include "feature/matching.h"
38 #include "mvs/fusion.h"
39 #include "mvs/meshing.h"
40 #include "mvs/patch_match.h"
41 #include "util/misc.h"
42 #include "util/option_manager.h"
43 
44 namespace colmap {
45 
AutomaticReconstructionController(const Options & options,ReconstructionManager * reconstruction_manager)46 AutomaticReconstructionController::AutomaticReconstructionController(
47     const Options& options, ReconstructionManager* reconstruction_manager)
48     : options_(options),
49       reconstruction_manager_(reconstruction_manager),
50       active_thread_(nullptr) {
51   CHECK(ExistsDir(options_.workspace_path));
52   CHECK(ExistsDir(options_.image_path));
53   CHECK_NOTNULL(reconstruction_manager_);
54 
55   option_manager_.AddAllOptions();
56 
57   *option_manager_.image_path = options_.image_path;
58   *option_manager_.database_path =
59       JoinPaths(options_.workspace_path, "database.db");
60 
61   if (options_.data_type == DataType::VIDEO) {
62     option_manager_.ModifyForVideoData();
63   } else if (options_.data_type == DataType::INDIVIDUAL) {
64     option_manager_.ModifyForIndividualData();
65   } else if (options_.data_type == DataType::INTERNET) {
66     option_manager_.ModifyForInternetData();
67   } else {
68     LOG(FATAL) << "Data type not supported";
69   }
70 
71   CHECK(ExistsCameraModelWithName(options_.camera_model));
72 
73   if (options_.quality == Quality::LOW) {
74     option_manager_.ModifyForLowQuality();
75   } else if (options_.quality == Quality::MEDIUM) {
76     option_manager_.ModifyForMediumQuality();
77   } else if (options_.quality == Quality::HIGH) {
78     option_manager_.ModifyForHighQuality();
79   } else if (options_.quality == Quality::EXTREME) {
80     option_manager_.ModifyForExtremeQuality();
81   }
82 
83   option_manager_.sift_extraction->num_threads = options_.num_threads;
84   option_manager_.sift_matching->num_threads = options_.num_threads;
85   option_manager_.mapper->num_threads = options_.num_threads;
86   option_manager_.poisson_meshing->num_threads = options_.num_threads;
87 
88   ImageReaderOptions reader_options = *option_manager_.image_reader;
89   reader_options.database_path = *option_manager_.database_path;
90   reader_options.image_path = *option_manager_.image_path;
91   if (!options_.mask_path.empty()) {
92     reader_options.mask_path = options_.mask_path;
93     option_manager_.image_reader->mask_path = options_.mask_path;
94   }
95   reader_options.single_camera = options_.single_camera;
96   reader_options.camera_model = options_.camera_model;
97 
98   option_manager_.sift_extraction->use_gpu = options_.use_gpu;
99   option_manager_.sift_matching->use_gpu = options_.use_gpu;
100 
101   option_manager_.sift_extraction->gpu_index = options_.gpu_index;
102   option_manager_.sift_matching->gpu_index = options_.gpu_index;
103   option_manager_.patch_match_stereo->gpu_index = options_.gpu_index;
104 
105   feature_extractor_.reset(new SiftFeatureExtractor(
106       reader_options, *option_manager_.sift_extraction));
107 
108   exhaustive_matcher_.reset(new ExhaustiveFeatureMatcher(
109       *option_manager_.exhaustive_matching, *option_manager_.sift_matching,
110       *option_manager_.database_path));
111 
112   if (!options_.vocab_tree_path.empty()) {
113     option_manager_.sequential_matching->loop_detection = true;
114     option_manager_.sequential_matching->vocab_tree_path =
115         options_.vocab_tree_path;
116   }
117 
118   sequential_matcher_.reset(new SequentialFeatureMatcher(
119       *option_manager_.sequential_matching, *option_manager_.sift_matching,
120       *option_manager_.database_path));
121 
122   if (!options_.vocab_tree_path.empty()) {
123     option_manager_.vocab_tree_matching->vocab_tree_path =
124         options_.vocab_tree_path;
125     vocab_tree_matcher_.reset(new VocabTreeFeatureMatcher(
126         *option_manager_.vocab_tree_matching, *option_manager_.sift_matching,
127         *option_manager_.database_path));
128   }
129 }
130 
Stop()131 void AutomaticReconstructionController::Stop() {
132   if (active_thread_ != nullptr) {
133     active_thread_->Stop();
134   }
135   Thread::Stop();
136 }
137 
Run()138 void AutomaticReconstructionController::Run() {
139   if (IsStopped()) {
140     return;
141   }
142 
143   RunFeatureExtraction();
144 
145   if (IsStopped()) {
146     return;
147   }
148 
149   RunFeatureMatching();
150 
151   if (IsStopped()) {
152     return;
153   }
154 
155   if (options_.sparse) {
156     RunSparseMapper();
157   }
158 
159   if (IsStopped()) {
160     return;
161   }
162 
163   if (options_.dense) {
164     RunDenseMapper();
165   }
166 }
167 
RunFeatureExtraction()168 void AutomaticReconstructionController::RunFeatureExtraction() {
169   CHECK(feature_extractor_);
170   active_thread_ = feature_extractor_.get();
171   feature_extractor_->Start();
172   feature_extractor_->Wait();
173   feature_extractor_.reset();
174   active_thread_ = nullptr;
175 }
176 
RunFeatureMatching()177 void AutomaticReconstructionController::RunFeatureMatching() {
178   Thread* matcher = nullptr;
179   if (options_.data_type == DataType::VIDEO) {
180     matcher = sequential_matcher_.get();
181   } else if (options_.data_type == DataType::INDIVIDUAL ||
182              options_.data_type == DataType::INTERNET) {
183     Database database(*option_manager_.database_path);
184     const size_t num_images = database.NumImages();
185     if (options_.vocab_tree_path.empty() || num_images < 200) {
186       matcher = exhaustive_matcher_.get();
187     } else {
188       matcher = vocab_tree_matcher_.get();
189     }
190   }
191 
192   CHECK(matcher);
193   active_thread_ = matcher;
194   matcher->Start();
195   matcher->Wait();
196   exhaustive_matcher_.reset();
197   sequential_matcher_.reset();
198   vocab_tree_matcher_.reset();
199   active_thread_ = nullptr;
200 }
201 
RunSparseMapper()202 void AutomaticReconstructionController::RunSparseMapper() {
203   const auto sparse_path = JoinPaths(options_.workspace_path, "sparse");
204   if (ExistsDir(sparse_path)) {
205     auto dir_list = GetDirList(sparse_path);
206     std::sort(dir_list.begin(), dir_list.end());
207     if (dir_list.size() > 0) {
208       std::cout << std::endl
209                 << "WARNING: Skipping sparse reconstruction because it is "
210                    "already computed"
211                 << std::endl;
212       for (const auto& dir : dir_list) {
213         reconstruction_manager_->Read(dir);
214       }
215       return;
216     }
217   }
218 
219   IncrementalMapperController mapper(
220       option_manager_.mapper.get(), *option_manager_.image_path,
221       *option_manager_.database_path, reconstruction_manager_);
222   active_thread_ = &mapper;
223   mapper.Start();
224   mapper.Wait();
225   active_thread_ = nullptr;
226 
227   CreateDirIfNotExists(sparse_path);
228   reconstruction_manager_->Write(sparse_path, &option_manager_);
229 }
230 
RunDenseMapper()231 void AutomaticReconstructionController::RunDenseMapper() {
232   CreateDirIfNotExists(JoinPaths(options_.workspace_path, "dense"));
233 
234   for (size_t i = 0; i < reconstruction_manager_->Size(); ++i) {
235     if (IsStopped()) {
236       return;
237     }
238 
239     const std::string dense_path =
240         JoinPaths(options_.workspace_path, "dense", std::to_string(i));
241     const std::string fused_path = JoinPaths(dense_path, "fused.ply");
242 
243     std::string meshing_path;
244     if (options_.mesher == Mesher::POISSON) {
245       meshing_path = JoinPaths(dense_path, "meshed-poisson.ply");
246     } else if (options_.mesher == Mesher::DELAUNAY) {
247       meshing_path = JoinPaths(dense_path, "meshed-delaunay.ply");
248     }
249 
250     if (ExistsFile(fused_path) && ExistsFile(meshing_path)) {
251       continue;
252     }
253 
254     // Image undistortion.
255 
256     if (!ExistsDir(dense_path)) {
257       CreateDirIfNotExists(dense_path);
258 
259       UndistortCameraOptions undistortion_options;
260       undistortion_options.max_image_size =
261           option_manager_.patch_match_stereo->max_image_size;
262       COLMAPUndistorter undistorter(undistortion_options,
263                                     reconstruction_manager_->Get(i),
264                                     *option_manager_.image_path, dense_path);
265       active_thread_ = &undistorter;
266       undistorter.Start();
267       undistorter.Wait();
268       active_thread_ = nullptr;
269     }
270 
271     if (IsStopped()) {
272       return;
273     }
274 
275     // Patch match stereo.
276 
277 #ifdef CUDA_ENABLED
278     {
279       mvs::PatchMatchController patch_match_controller(
280           *option_manager_.patch_match_stereo, dense_path, "COLMAP", "");
281       active_thread_ = &patch_match_controller;
282       patch_match_controller.Start();
283       patch_match_controller.Wait();
284       active_thread_ = nullptr;
285     }
286 #else   // CUDA_ENABLED
287     std::cout
288         << std::endl
289         << "WARNING: Skipping patch match stereo because CUDA is not available."
290         << std::endl;
291     return;
292 #endif  // CUDA_ENABLED
293 
294     if (IsStopped()) {
295       return;
296     }
297 
298     // Stereo fusion.
299 
300     if (!ExistsFile(fused_path)) {
301       auto fusion_options = *option_manager_.stereo_fusion;
302       const int num_reg_images = reconstruction_manager_->Get(i).NumRegImages();
303       fusion_options.min_num_pixels =
304           std::min(num_reg_images + 1, fusion_options.min_num_pixels);
305       mvs::StereoFusion fuser(
306           fusion_options, dense_path, "COLMAP", "",
307           options_.quality == Quality::HIGH ? "geometric" : "photometric");
308       active_thread_ = &fuser;
309       fuser.Start();
310       fuser.Wait();
311       active_thread_ = nullptr;
312 
313       std::cout << "Writing output: " << fused_path << std::endl;
314       WriteBinaryPlyPoints(fused_path, fuser.GetFusedPoints());
315       mvs::WritePointsVisibility(fused_path + ".vis",
316                                  fuser.GetFusedPointsVisibility());
317     }
318 
319     if (IsStopped()) {
320       return;
321     }
322 
323     // Surface meshing.
324 
325     if (!ExistsFile(meshing_path)) {
326       if (options_.mesher == Mesher::POISSON) {
327         mvs::PoissonMeshing(*option_manager_.poisson_meshing, fused_path,
328                             meshing_path);
329       } else if (options_.mesher == Mesher::DELAUNAY) {
330 #ifdef CGAL_ENABLED
331         mvs::DenseDelaunayMeshing(*option_manager_.delaunay_meshing, dense_path,
332                                   meshing_path);
333 #else  // CGAL_ENABLED
334         std::cout << std::endl
335                   << "WARNING: Skipping Delaunay meshing because CGAL is "
336                      "not available."
337                   << std::endl;
338         return;
339 
340 #endif  // CGAL_ENABLED
341       }
342     }
343   }
344 }
345 
346 }  // namespace colmap
347