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