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 #ifndef NOMINMAX
33 #define NOMINMAX
34 #endif
35
36 #include <boost/property_tree/json_parser.hpp>
37 #include <boost/property_tree/ptree.hpp>
38
39 #include "base/similarity_transform.h"
40 #include "controllers/automatic_reconstruction.h"
41 #include "controllers/bundle_adjustment.h"
42 #include "controllers/hierarchical_mapper.h"
43 #include "estimators/coordinate_frame.h"
44 #include "feature/extraction.h"
45 #include "feature/matching.h"
46 #include "feature/utils.h"
47 #include "mvs/meshing.h"
48 #include "mvs/patch_match.h"
49 #include "retrieval/visual_index.h"
50 #include "ui/main_window.h"
51 #include "util/opengl_utils.h"
52 #include "util/version.h"
53
54 using namespace colmap;
55
56 #ifdef CUDA_ENABLED
57 const bool kUseOpenGL = false;
58 #else
59 const bool kUseOpenGL = true;
60 #endif
61
RunGraphicalUserInterface(int argc,char ** argv)62 int RunGraphicalUserInterface(int argc, char** argv) {
63 OptionManager options;
64
65 std::string import_path;
66
67 if (argc > 1) {
68 options.AddDefaultOption("import_path", &import_path);
69 options.AddAllOptions();
70 options.Parse(argc, argv);
71 }
72
73 #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
74 QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
75 QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
76 #endif
77
78 Q_INIT_RESOURCE(resources);
79
80 QApplication app(argc, argv);
81
82 MainWindow main_window(options);
83 main_window.show();
84
85 if (!import_path.empty()) {
86 main_window.ImportReconstruction(import_path);
87 }
88
89 return app.exec();
90 }
91
RunAutomaticReconstructor(int argc,char ** argv)92 int RunAutomaticReconstructor(int argc, char** argv) {
93 AutomaticReconstructionController::Options reconstruction_options;
94 std::string data_type = "individual";
95 std::string quality = "high";
96 std::string mesher = "poisson";
97
98 OptionManager options;
99 options.AddRequiredOption("workspace_path",
100 &reconstruction_options.workspace_path);
101 options.AddRequiredOption("image_path", &reconstruction_options.image_path);
102 options.AddDefaultOption("mask_path", &reconstruction_options.mask_path);
103 options.AddDefaultOption("vocab_tree_path",
104 &reconstruction_options.vocab_tree_path);
105 options.AddDefaultOption("data_type", &data_type,
106 "{individual, video, internet}");
107 options.AddDefaultOption("quality", &quality, "{low, medium, high, extreme}");
108 options.AddDefaultOption("camera_model",
109 &reconstruction_options.camera_model);
110 options.AddDefaultOption("single_camera",
111 &reconstruction_options.single_camera);
112 options.AddDefaultOption("sparse", &reconstruction_options.sparse);
113 options.AddDefaultOption("dense", &reconstruction_options.dense);
114 options.AddDefaultOption("mesher", &mesher, "{poisson, delaunay}");
115 options.AddDefaultOption("num_threads", &reconstruction_options.num_threads);
116 options.AddDefaultOption("use_gpu", &reconstruction_options.use_gpu);
117 options.AddDefaultOption("gpu_index", &reconstruction_options.gpu_index);
118 options.Parse(argc, argv);
119
120 StringToLower(&data_type);
121 if (data_type == "individual") {
122 reconstruction_options.data_type =
123 AutomaticReconstructionController::DataType::INDIVIDUAL;
124 } else if (data_type == "video") {
125 reconstruction_options.data_type =
126 AutomaticReconstructionController::DataType::VIDEO;
127 } else if (data_type == "internet") {
128 reconstruction_options.data_type =
129 AutomaticReconstructionController::DataType::INTERNET;
130 } else {
131 LOG(FATAL) << "Invalid data type provided";
132 }
133
134 StringToLower(&quality);
135 if (quality == "low") {
136 reconstruction_options.quality =
137 AutomaticReconstructionController::Quality::LOW;
138 } else if (quality == "medium") {
139 reconstruction_options.quality =
140 AutomaticReconstructionController::Quality::MEDIUM;
141 } else if (quality == "high") {
142 reconstruction_options.quality =
143 AutomaticReconstructionController::Quality::HIGH;
144 } else if (quality == "extreme") {
145 reconstruction_options.quality =
146 AutomaticReconstructionController::Quality::EXTREME;
147 } else {
148 LOG(FATAL) << "Invalid quality provided";
149 }
150
151 StringToLower(&mesher);
152 if (mesher == "poisson") {
153 reconstruction_options.mesher =
154 AutomaticReconstructionController::Mesher::POISSON;
155 } else if (mesher == "delaunay") {
156 reconstruction_options.mesher =
157 AutomaticReconstructionController::Mesher::DELAUNAY;
158 } else {
159 LOG(FATAL) << "Invalid mesher provided";
160 }
161
162 ReconstructionManager reconstruction_manager;
163
164 if (reconstruction_options.use_gpu && kUseOpenGL) {
165 QApplication app(argc, argv);
166 AutomaticReconstructionController controller(reconstruction_options,
167 &reconstruction_manager);
168 RunThreadWithOpenGLContext(&controller);
169 } else {
170 AutomaticReconstructionController controller(reconstruction_options,
171 &reconstruction_manager);
172 controller.Start();
173 controller.Wait();
174 }
175
176 return EXIT_SUCCESS;
177 }
178
RunBundleAdjuster(int argc,char ** argv)179 int RunBundleAdjuster(int argc, char** argv) {
180 std::string input_path;
181 std::string output_path;
182
183 OptionManager options;
184 options.AddRequiredOption("input_path", &input_path);
185 options.AddRequiredOption("output_path", &output_path);
186 options.AddBundleAdjustmentOptions();
187 options.Parse(argc, argv);
188
189 if (!ExistsDir(input_path)) {
190 std::cerr << "ERROR: `input_path` is not a directory" << std::endl;
191 return EXIT_FAILURE;
192 }
193
194 if (!ExistsDir(output_path)) {
195 std::cerr << "ERROR: `output_path` is not a directory" << std::endl;
196 return EXIT_FAILURE;
197 }
198
199 Reconstruction reconstruction;
200 reconstruction.Read(input_path);
201
202 BundleAdjustmentController ba_controller(options, &reconstruction);
203 ba_controller.Start();
204 ba_controller.Wait();
205
206 reconstruction.Write(output_path);
207
208 return EXIT_SUCCESS;
209 }
210
RunColorExtractor(int argc,char ** argv)211 int RunColorExtractor(int argc, char** argv) {
212 std::string input_path;
213 std::string output_path;
214
215 OptionManager options;
216 options.AddImageOptions();
217 options.AddDefaultOption("input_path", &input_path);
218 options.AddRequiredOption("output_path", &output_path);
219 options.Parse(argc, argv);
220
221 Reconstruction reconstruction;
222 reconstruction.Read(input_path);
223 reconstruction.ExtractColorsForAllImages(*options.image_path);
224 reconstruction.Write(output_path);
225
226 return EXIT_SUCCESS;
227 }
228
RunDatabaseCreator(int argc,char ** argv)229 int RunDatabaseCreator(int argc, char** argv) {
230 OptionManager options;
231 options.AddDatabaseOptions();
232 options.Parse(argc, argv);
233
234 Database database(*options.database_path);
235
236 return EXIT_SUCCESS;
237 }
238
RunDatabaseMerger(int argc,char ** argv)239 int RunDatabaseMerger(int argc, char** argv) {
240 std::string database_path1;
241 std::string database_path2;
242 std::string merged_database_path;
243
244 OptionManager options;
245 options.AddRequiredOption("database_path1", &database_path1);
246 options.AddRequiredOption("database_path2", &database_path2);
247 options.AddRequiredOption("merged_database_path", &merged_database_path);
248 options.Parse(argc, argv);
249
250 if (ExistsFile(merged_database_path)) {
251 std::cout << "ERROR: Merged database file must not exist." << std::endl;
252 return EXIT_FAILURE;
253 }
254
255 Database database1(database_path1);
256 Database database2(database_path2);
257 Database merged_database(merged_database_path);
258 Database::Merge(database1, database2, &merged_database);
259
260 return EXIT_SUCCESS;
261 }
262
RunStereoFuser(int argc,char ** argv)263 int RunStereoFuser(int argc, char** argv) {
264 std::string workspace_path;
265 std::string input_type = "geometric";
266 std::string workspace_format = "COLMAP";
267 std::string pmvs_option_name = "option-all";
268 std::string output_path;
269
270 OptionManager options;
271 options.AddRequiredOption("workspace_path", &workspace_path);
272 options.AddDefaultOption("workspace_format", &workspace_format,
273 "{COLMAP, PMVS}");
274 options.AddDefaultOption("pmvs_option_name", &pmvs_option_name);
275 options.AddDefaultOption("input_type", &input_type,
276 "{photometric, geometric}");
277 options.AddRequiredOption("output_path", &output_path);
278 options.AddStereoFusionOptions();
279 options.Parse(argc, argv);
280
281 StringToLower(&workspace_format);
282 if (workspace_format != "colmap" && workspace_format != "pmvs") {
283 std::cout << "ERROR: Invalid `workspace_format` - supported values are "
284 "'COLMAP' or 'PMVS'."
285 << std::endl;
286 return EXIT_FAILURE;
287 }
288
289 StringToLower(&input_type);
290 if (input_type != "photometric" && input_type != "geometric") {
291 std::cout << "ERROR: Invalid input type - supported values are "
292 "'photometric' and 'geometric'."
293 << std::endl;
294 return EXIT_FAILURE;
295 }
296
297 mvs::StereoFusion fuser(*options.stereo_fusion, workspace_path,
298 workspace_format, pmvs_option_name, input_type);
299
300 fuser.Start();
301 fuser.Wait();
302
303 std::cout << "Writing output: " << output_path << std::endl;
304 WriteBinaryPlyPoints(output_path, fuser.GetFusedPoints());
305 mvs::WritePointsVisibility(output_path + ".vis",
306 fuser.GetFusedPointsVisibility());
307
308 return EXIT_SUCCESS;
309 }
310
RunPoissonMesher(int argc,char ** argv)311 int RunPoissonMesher(int argc, char** argv) {
312 std::string input_path;
313 std::string output_path;
314
315 OptionManager options;
316 options.AddRequiredOption("input_path", &input_path);
317 options.AddRequiredOption("output_path", &output_path);
318 options.AddPoissonMeshingOptions();
319 options.Parse(argc, argv);
320
321 CHECK(mvs::PoissonMeshing(*options.poisson_meshing, input_path, output_path));
322
323 return EXIT_SUCCESS;
324 }
325
RunProjectGenerator(int argc,char ** argv)326 int RunProjectGenerator(int argc, char** argv) {
327 std::string output_path;
328 std::string quality = "high";
329
330 OptionManager options;
331 options.AddRequiredOption("output_path", &output_path);
332 options.AddDefaultOption("quality", &quality, "{low, medium, high, extreme}");
333 options.Parse(argc, argv);
334
335 OptionManager output_options;
336 output_options.AddAllOptions();
337
338 StringToLower(&quality);
339 if (quality == "low") {
340 output_options.ModifyForLowQuality();
341 } else if (quality == "medium") {
342 output_options.ModifyForMediumQuality();
343 } else if (quality == "high") {
344 output_options.ModifyForHighQuality();
345 } else if (quality == "extreme") {
346 output_options.ModifyForExtremeQuality();
347 } else {
348 LOG(FATAL) << "Invalid quality provided";
349 }
350
351 output_options.Write(output_path);
352
353 return EXIT_SUCCESS;
354 }
355
RunDelaunayMesher(int argc,char ** argv)356 int RunDelaunayMesher(int argc, char** argv) {
357 #ifndef CGAL_ENABLED
358 std::cerr << "ERROR: Delaunay meshing requires CGAL, which is not "
359 "available on your system."
360 << std::endl;
361 return EXIT_FAILURE;
362 #else // CGAL_ENABLED
363 std::string input_path;
364 std::string input_type = "dense";
365 std::string output_path;
366
367 OptionManager options;
368 options.AddRequiredOption(
369 "input_path", &input_path,
370 "Path to either the dense workspace folder or the sparse reconstruction");
371 options.AddDefaultOption("input_type", &input_type, "{dense, sparse}");
372 options.AddRequiredOption("output_path", &output_path);
373 options.AddDelaunayMeshingOptions();
374 options.Parse(argc, argv);
375
376 StringToLower(&input_type);
377 if (input_type == "sparse") {
378 mvs::SparseDelaunayMeshing(*options.delaunay_meshing, input_path,
379 output_path);
380 } else if (input_type == "dense") {
381 mvs::DenseDelaunayMeshing(*options.delaunay_meshing, input_path,
382 output_path);
383 } else {
384 std::cout << "ERROR: Invalid input type - "
385 "supported values are 'sparse' and 'dense'."
386 << std::endl;
387 return EXIT_FAILURE;
388 }
389
390 return EXIT_SUCCESS;
391 #endif // CGAL_ENABLED
392 }
393
RunPatchMatchStereo(int argc,char ** argv)394 int RunPatchMatchStereo(int argc, char** argv) {
395 #ifndef CUDA_ENABLED
396 std::cerr << "ERROR: Dense stereo reconstruction requires CUDA, which is not "
397 "available on your system."
398 << std::endl;
399 return EXIT_FAILURE;
400 #else // CUDA_ENABLED
401 std::string workspace_path;
402 std::string workspace_format = "COLMAP";
403 std::string pmvs_option_name = "option-all";
404
405 OptionManager options;
406 options.AddRequiredOption(
407 "workspace_path", &workspace_path,
408 "Path to the folder containing the undistorted images");
409 options.AddDefaultOption("workspace_format", &workspace_format,
410 "{COLMAP, PMVS}");
411 options.AddDefaultOption("pmvs_option_name", &pmvs_option_name);
412 options.AddPatchMatchStereoOptions();
413 options.Parse(argc, argv);
414
415 StringToLower(&workspace_format);
416 if (workspace_format != "colmap" && workspace_format != "pmvs") {
417 std::cout << "ERROR: Invalid `workspace_format` - supported values are "
418 "'COLMAP' or 'PMVS'."
419 << std::endl;
420 return EXIT_FAILURE;
421 }
422
423 mvs::PatchMatchController controller(*options.patch_match_stereo,
424 workspace_path, workspace_format,
425 pmvs_option_name);
426
427 controller.Start();
428 controller.Wait();
429
430 return EXIT_SUCCESS;
431 #endif // CUDA_ENABLED
432 }
433
RunExhaustiveMatcher(int argc,char ** argv)434 int RunExhaustiveMatcher(int argc, char** argv) {
435 OptionManager options;
436 options.AddDatabaseOptions();
437 options.AddExhaustiveMatchingOptions();
438 options.Parse(argc, argv);
439
440 std::unique_ptr<QApplication> app;
441 if (options.sift_matching->use_gpu && kUseOpenGL) {
442 app.reset(new QApplication(argc, argv));
443 }
444
445 ExhaustiveFeatureMatcher feature_matcher(*options.exhaustive_matching,
446 *options.sift_matching,
447 *options.database_path);
448
449 if (options.sift_matching->use_gpu && kUseOpenGL) {
450 RunThreadWithOpenGLContext(&feature_matcher);
451 } else {
452 feature_matcher.Start();
453 feature_matcher.Wait();
454 }
455
456 return EXIT_SUCCESS;
457 }
458
VerifyCameraParams(const std::string & camera_model,const std::string & params)459 bool VerifyCameraParams(const std::string& camera_model,
460 const std::string& params) {
461 if (!ExistsCameraModelWithName(camera_model)) {
462 std::cerr << "ERROR: Camera model does not exist" << std::endl;
463 return false;
464 }
465
466 const std::vector<double> camera_params = CSVToVector<double>(params);
467 const int camera_model_id = CameraModelNameToId(camera_model);
468
469 if (camera_params.size() > 0 &&
470 !CameraModelVerifyParams(camera_model_id, camera_params)) {
471 std::cerr << "ERROR: Invalid camera parameters" << std::endl;
472 return false;
473 }
474 return true;
475 }
476
RunFeatureExtractor(int argc,char ** argv)477 int RunFeatureExtractor(int argc, char** argv) {
478 std::string image_list_path;
479
480 OptionManager options;
481 options.AddDatabaseOptions();
482 options.AddImageOptions();
483 options.AddDefaultOption("image_list_path", &image_list_path);
484 options.AddExtractionOptions();
485 options.Parse(argc, argv);
486
487 ImageReaderOptions reader_options = *options.image_reader;
488 reader_options.database_path = *options.database_path;
489 reader_options.image_path = *options.image_path;
490
491 if (!image_list_path.empty()) {
492 reader_options.image_list = ReadTextFileLines(image_list_path);
493 if (reader_options.image_list.empty()) {
494 return EXIT_SUCCESS;
495 }
496 }
497
498 if (!ExistsCameraModelWithName(options.image_reader->camera_model)) {
499 std::cerr << "ERROR: Camera model does not exist" << std::endl;
500 }
501
502 if (!VerifyCameraParams(options.image_reader->camera_model,
503 options.image_reader->camera_params)) {
504 return EXIT_FAILURE;
505 }
506
507 std::unique_ptr<QApplication> app;
508 if (options.sift_extraction->use_gpu && kUseOpenGL) {
509 app.reset(new QApplication(argc, argv));
510 }
511
512 SiftFeatureExtractor feature_extractor(reader_options,
513 *options.sift_extraction);
514
515 if (options.sift_extraction->use_gpu && kUseOpenGL) {
516 RunThreadWithOpenGLContext(&feature_extractor);
517 } else {
518 feature_extractor.Start();
519 feature_extractor.Wait();
520 }
521
522 return EXIT_SUCCESS;
523 }
524
RunFeatureImporter(int argc,char ** argv)525 int RunFeatureImporter(int argc, char** argv) {
526 std::string import_path;
527 std::string image_list_path;
528
529 OptionManager options;
530 options.AddDatabaseOptions();
531 options.AddImageOptions();
532 options.AddRequiredOption("import_path", &import_path);
533 options.AddDefaultOption("image_list_path", &image_list_path);
534 options.AddExtractionOptions();
535 options.Parse(argc, argv);
536
537 ImageReaderOptions reader_options = *options.image_reader;
538 reader_options.database_path = *options.database_path;
539 reader_options.image_path = *options.image_path;
540
541 if (!image_list_path.empty()) {
542 reader_options.image_list = ReadTextFileLines(image_list_path);
543 if (reader_options.image_list.empty()) {
544 return EXIT_SUCCESS;
545 }
546 }
547
548 if (!VerifyCameraParams(options.image_reader->camera_model,
549 options.image_reader->camera_params)) {
550 return EXIT_FAILURE;
551 }
552
553 FeatureImporter feature_importer(reader_options, import_path);
554 feature_importer.Start();
555 feature_importer.Wait();
556
557 return EXIT_SUCCESS;
558 }
559
560 // Read stereo image pair names from a text file. The text file is expected to
561 // have one image pair per line, e.g.:
562 //
563 // image_name1.jpg image_name2.jpg
564 // image_name3.jpg image_name4.jpg
565 // image_name5.jpg image_name6.jpg
566 // ...
567 //
ReadStereoImagePairs(const std::string & path,const Reconstruction & reconstruction)568 std::vector<std::pair<image_t, image_t>> ReadStereoImagePairs(
569 const std::string& path, const Reconstruction& reconstruction) {
570 const std::vector<std::string> stereo_pair_lines = ReadTextFileLines(path);
571
572 std::vector<std::pair<image_t, image_t>> stereo_pairs;
573 stereo_pairs.reserve(stereo_pair_lines.size());
574
575 for (const auto& line : stereo_pair_lines) {
576 const std::vector<std::string> names = StringSplit(line, " ");
577 CHECK_EQ(names.size(), 2);
578
579 const Image* image1 = reconstruction.FindImageWithName(names[0]);
580 const Image* image2 = reconstruction.FindImageWithName(names[1]);
581
582 CHECK_NOTNULL(image1);
583 CHECK_NOTNULL(image2);
584
585 stereo_pairs.emplace_back(image1->ImageId(), image2->ImageId());
586 }
587
588 return stereo_pairs;
589 }
590
RunImageDeleter(int argc,char ** argv)591 int RunImageDeleter(int argc, char** argv) {
592 std::string input_path;
593 std::string output_path;
594 std::string image_ids_path;
595 std::string image_names_path;
596
597 OptionManager options;
598 options.AddRequiredOption("input_path", &input_path);
599 options.AddRequiredOption("output_path", &output_path);
600 options.AddDefaultOption(
601 "image_ids_path", &image_ids_path,
602 "Path to text file containing one image_id to delete per line");
603 options.AddDefaultOption(
604 "image_names_path", &image_names_path,
605 "Path to text file containing one image name to delete per line");
606 options.Parse(argc, argv);
607
608 Reconstruction reconstruction;
609 reconstruction.Read(input_path);
610
611 if (!image_ids_path.empty()) {
612 const auto image_ids = ReadTextFileLines(image_ids_path);
613
614 for (const auto image_id_str : image_ids) {
615 if (image_id_str.empty()) {
616 continue;
617 }
618
619 const image_t image_id = std::stoi(image_id_str);
620 if (reconstruction.ExistsImage(image_id)) {
621 const auto& image = reconstruction.Image(image_id);
622 std::cout
623 << StringPrintf(
624 "Deleting image_id=%d, image_name=%s from reconstruction",
625 image.ImageId(), image.Name().c_str())
626 << std::endl;
627 reconstruction.DeRegisterImage(image_id);
628 } else {
629 std::cout << StringPrintf(
630 "WARNING: Skipping image_id=%s, because it does not "
631 "exist in the reconstruction",
632 image_id_str.c_str())
633 << std::endl;
634 }
635 }
636 }
637
638 if (!image_names_path.empty()) {
639 const auto image_names = ReadTextFileLines(image_names_path);
640
641 for (const auto image_name : image_names) {
642 if (image_name.empty()) {
643 continue;
644 }
645
646 const Image* image = reconstruction.FindImageWithName(image_name);
647 if (image != nullptr) {
648 std::cout
649 << StringPrintf(
650 "Deleting image_id=%d, image_name=%s from reconstruction",
651 image->ImageId(), image->Name().c_str())
652 << std::endl;
653 reconstruction.DeRegisterImage(image->ImageId());
654 } else {
655 std::cout << StringPrintf(
656 "WARNING: Skipping image_name=%s, because it does not "
657 "exist in the reconstruction",
658 image_name.c_str())
659 << std::endl;
660 }
661 }
662 }
663
664 reconstruction.Write(output_path);
665
666 return EXIT_SUCCESS;
667 }
668
RunImageFilterer(int argc,char ** argv)669 int RunImageFilterer(int argc, char** argv) {
670 std::string input_path;
671 std::string output_path;
672 double min_focal_length_ratio = 0.1;
673 double max_focal_length_ratio = 10.0;
674 double max_extra_param = 100.0;
675 size_t min_num_observations = 10;
676
677 OptionManager options;
678 options.AddRequiredOption("input_path", &input_path);
679 options.AddRequiredOption("output_path", &output_path);
680 options.AddDefaultOption("min_focal_length_ratio", &min_focal_length_ratio);
681 options.AddDefaultOption("max_focal_length_ratio", &max_focal_length_ratio);
682 options.AddDefaultOption("max_extra_param", &max_extra_param);
683 options.AddDefaultOption("min_num_observations", &min_num_observations);
684 options.Parse(argc, argv);
685
686 Reconstruction reconstruction;
687 reconstruction.Read(input_path);
688
689 const size_t num_reg_images = reconstruction.NumRegImages();
690
691 reconstruction.FilterImages(min_focal_length_ratio, max_focal_length_ratio,
692 max_extra_param);
693
694 std::vector<image_t> filtered_image_ids;
695 for (const auto& image : reconstruction.Images()) {
696 if (image.second.IsRegistered() &&
697 image.second.NumPoints3D() < min_num_observations) {
698 filtered_image_ids.push_back(image.first);
699 }
700 }
701
702 for (const auto image_id : filtered_image_ids) {
703 reconstruction.DeRegisterImage(image_id);
704 }
705
706 const size_t num_filtered_images =
707 num_reg_images - reconstruction.NumRegImages();
708
709 std::cout << StringPrintf("Filtered %d images from a total of %d images",
710 num_filtered_images, num_reg_images)
711 << std::endl;
712
713 reconstruction.Write(output_path);
714
715 return EXIT_SUCCESS;
716 }
717
RunImageRectifier(int argc,char ** argv)718 int RunImageRectifier(int argc, char** argv) {
719 std::string input_path;
720 std::string output_path;
721 std::string stereo_pairs_list;
722
723 UndistortCameraOptions undistort_camera_options;
724
725 OptionManager options;
726 options.AddImageOptions();
727 options.AddRequiredOption("input_path", &input_path);
728 options.AddRequiredOption("output_path", &output_path);
729 options.AddRequiredOption("stereo_pairs_list", &stereo_pairs_list);
730 options.AddDefaultOption("blank_pixels",
731 &undistort_camera_options.blank_pixels);
732 options.AddDefaultOption("min_scale", &undistort_camera_options.min_scale);
733 options.AddDefaultOption("max_scale", &undistort_camera_options.max_scale);
734 options.AddDefaultOption("max_image_size",
735 &undistort_camera_options.max_image_size);
736 options.Parse(argc, argv);
737
738 Reconstruction reconstruction;
739 reconstruction.Read(input_path);
740
741 const auto stereo_pairs =
742 ReadStereoImagePairs(stereo_pairs_list, reconstruction);
743
744 StereoImageRectifier rectifier(undistort_camera_options, reconstruction,
745 *options.image_path, output_path,
746 stereo_pairs);
747 rectifier.Start();
748 rectifier.Wait();
749
750 return EXIT_SUCCESS;
751 }
752
RunImageRegistrator(int argc,char ** argv)753 int RunImageRegistrator(int argc, char** argv) {
754 std::string input_path;
755 std::string output_path;
756
757 OptionManager options;
758 options.AddDatabaseOptions();
759 options.AddRequiredOption("input_path", &input_path);
760 options.AddRequiredOption("output_path", &output_path);
761 options.AddMapperOptions();
762 options.Parse(argc, argv);
763
764 if (!ExistsDir(input_path)) {
765 std::cerr << "ERROR: `input_path` is not a directory" << std::endl;
766 return EXIT_FAILURE;
767 }
768
769 if (!ExistsDir(output_path)) {
770 std::cerr << "ERROR: `output_path` is not a directory" << std::endl;
771 return EXIT_FAILURE;
772 }
773
774 PrintHeading1("Loading database");
775
776 DatabaseCache database_cache;
777
778 {
779 Database database(*options.database_path);
780 Timer timer;
781 timer.Start();
782 const size_t min_num_matches =
783 static_cast<size_t>(options.mapper->min_num_matches);
784 database_cache.Load(database, min_num_matches,
785 options.mapper->ignore_watermarks,
786 options.mapper->image_names);
787 std::cout << std::endl;
788 timer.PrintMinutes();
789 }
790
791 std::cout << std::endl;
792
793 Reconstruction reconstruction;
794 reconstruction.Read(input_path);
795
796 IncrementalMapper mapper(&database_cache);
797 mapper.BeginReconstruction(&reconstruction);
798
799 const auto mapper_options = options.mapper->Mapper();
800
801 for (const auto& image : reconstruction.Images()) {
802 if (image.second.IsRegistered()) {
803 continue;
804 }
805
806 PrintHeading1("Registering image #" + std::to_string(image.first) + " (" +
807 std::to_string(reconstruction.NumRegImages() + 1) + ")");
808
809 std::cout << " => Image sees " << image.second.NumVisiblePoints3D()
810 << " / " << image.second.NumObservations() << " points"
811 << std::endl;
812
813 mapper.RegisterNextImage(mapper_options, image.first);
814 }
815
816 const bool kDiscardReconstruction = false;
817 mapper.EndReconstruction(kDiscardReconstruction);
818
819 reconstruction.Write(output_path);
820
821 return EXIT_SUCCESS;
822 }
823
RunImageUndistorter(int argc,char ** argv)824 int RunImageUndistorter(int argc, char** argv) {
825 std::string input_path;
826 std::string output_path;
827 std::string output_type = "COLMAP";
828
829 UndistortCameraOptions undistort_camera_options;
830
831 OptionManager options;
832 options.AddImageOptions();
833 options.AddRequiredOption("input_path", &input_path);
834 options.AddRequiredOption("output_path", &output_path);
835 options.AddDefaultOption("output_type", &output_type,
836 "{COLMAP, PMVS, CMP-MVS}");
837 options.AddDefaultOption("blank_pixels",
838 &undistort_camera_options.blank_pixels);
839 options.AddDefaultOption("min_scale", &undistort_camera_options.min_scale);
840 options.AddDefaultOption("max_scale", &undistort_camera_options.max_scale);
841 options.AddDefaultOption("max_image_size",
842 &undistort_camera_options.max_image_size);
843 options.AddDefaultOption("roi_min_x", &undistort_camera_options.roi_min_x);
844 options.AddDefaultOption("roi_min_y", &undistort_camera_options.roi_min_y);
845 options.AddDefaultOption("roi_max_x", &undistort_camera_options.roi_max_x);
846 options.AddDefaultOption("roi_max_y", &undistort_camera_options.roi_max_y);
847 options.Parse(argc, argv);
848
849 CreateDirIfNotExists(output_path);
850
851 Reconstruction reconstruction;
852 reconstruction.Read(input_path);
853
854 std::unique_ptr<Thread> undistorter;
855 if (output_type == "COLMAP") {
856 undistorter.reset(new COLMAPUndistorter(undistort_camera_options,
857 reconstruction, *options.image_path,
858 output_path));
859 } else if (output_type == "PMVS") {
860 undistorter.reset(new PMVSUndistorter(undistort_camera_options,
861 reconstruction, *options.image_path,
862 output_path));
863 } else if (output_type == "CMP-MVS") {
864 undistorter.reset(new CMPMVSUndistorter(undistort_camera_options,
865 reconstruction, *options.image_path,
866 output_path));
867 } else {
868 std::cerr << "ERROR: Invalid `output_type` - supported values are "
869 "{'COLMAP', 'PMVS', 'CMP-MVS'}."
870 << std::endl;
871 return EXIT_FAILURE;
872 }
873
874 undistorter->Start();
875 undistorter->Wait();
876
877 return EXIT_SUCCESS;
878 }
879
RunImageUndistorterStandalone(int argc,char ** argv)880 int RunImageUndistorterStandalone(int argc, char** argv) {
881 std::string input_file;
882 std::string output_path;
883
884 UndistortCameraOptions undistort_camera_options;
885
886 OptionManager options;
887 options.AddImageOptions();
888 options.AddRequiredOption("input_file", &input_file);
889 options.AddRequiredOption("output_path", &output_path);
890 options.AddDefaultOption("blank_pixels",
891 &undistort_camera_options.blank_pixels);
892 options.AddDefaultOption("min_scale", &undistort_camera_options.min_scale);
893 options.AddDefaultOption("max_scale", &undistort_camera_options.max_scale);
894 options.AddDefaultOption("max_image_size",
895 &undistort_camera_options.max_image_size);
896 options.AddDefaultOption("roi_min_x", &undistort_camera_options.roi_min_x);
897 options.AddDefaultOption("roi_min_y", &undistort_camera_options.roi_min_y);
898 options.AddDefaultOption("roi_max_x", &undistort_camera_options.roi_max_x);
899 options.AddDefaultOption("roi_max_y", &undistort_camera_options.roi_max_y);
900 options.Parse(argc, argv);
901
902 CreateDirIfNotExists(output_path);
903
904 // Loads a text file containing the image names and camera information.
905 // The format of the text file is
906 // image_name CAMERA_MODEL camera_params
907 std::vector<std::pair<std::string, Camera>> image_names_and_cameras;
908
909 {
910 std::ifstream file(input_file);
911 CHECK(file.is_open()) << input_file;
912
913 std::string line;
914 std::vector<std::string> lines;
915 while (std::getline(file, line)) {
916 StringTrim(&line);
917
918 if (line.empty()) {
919 continue;
920 }
921
922 std::string item;
923 std::stringstream line_stream(line);
924
925 // Loads the image name.
926 std::string image_name;
927 std::getline(line_stream, image_name, ' ');
928
929 // Loads the camera and its parameters
930 class Camera camera;
931
932 std::getline(line_stream, item, ' ');
933 if (!ExistsCameraModelWithName(item)) {
934 std::cerr << "ERROR: Camera model " << item << " does not exist"
935 << std::endl;
936 return EXIT_FAILURE;
937 }
938 camera.SetModelIdFromName(item);
939
940 std::getline(line_stream, item, ' ');
941 camera.SetWidth(std::stoll(item));
942
943 std::getline(line_stream, item, ' ');
944 camera.SetHeight(std::stoll(item));
945
946 camera.Params().clear();
947 while (!line_stream.eof()) {
948 std::getline(line_stream, item, ' ');
949 camera.Params().push_back(std::stold(item));
950 }
951
952 CHECK(camera.VerifyParams());
953
954 image_names_and_cameras.emplace_back(image_name, camera);
955 }
956 }
957
958 std::unique_ptr<Thread> undistorter;
959 undistorter.reset(new PureImageUndistorter(undistort_camera_options,
960 *options.image_path, output_path,
961 image_names_and_cameras));
962
963 undistorter->Start();
964 undistorter->Wait();
965
966 return EXIT_SUCCESS;
967 }
968
RunMapper(int argc,char ** argv)969 int RunMapper(int argc, char** argv) {
970 std::string input_path;
971 std::string output_path;
972 std::string image_list_path;
973
974 OptionManager options;
975 options.AddDatabaseOptions();
976 options.AddImageOptions();
977 options.AddDefaultOption("input_path", &input_path);
978 options.AddRequiredOption("output_path", &output_path);
979 options.AddDefaultOption("image_list_path", &image_list_path);
980 options.AddMapperOptions();
981 options.Parse(argc, argv);
982
983 if (!ExistsDir(output_path)) {
984 std::cerr << "ERROR: `output_path` is not a directory." << std::endl;
985 return EXIT_FAILURE;
986 }
987
988 if (!image_list_path.empty()) {
989 const auto image_names = ReadTextFileLines(image_list_path);
990 options.mapper->image_names =
991 std::unordered_set<std::string>(image_names.begin(), image_names.end());
992 }
993
994 ReconstructionManager reconstruction_manager;
995 if (input_path != "") {
996 if (!ExistsDir(input_path)) {
997 std::cerr << "ERROR: `input_path` is not a directory." << std::endl;
998 return EXIT_FAILURE;
999 }
1000 reconstruction_manager.Read(input_path);
1001 }
1002
1003 IncrementalMapperController mapper(options.mapper.get(), *options.image_path,
1004 *options.database_path,
1005 &reconstruction_manager);
1006
1007 // In case a new reconstruction is started, write results of individual sub-
1008 // models to as their reconstruction finishes instead of writing all results
1009 // after all reconstructions finished.
1010 size_t prev_num_reconstructions = 0;
1011 if (input_path == "") {
1012 mapper.AddCallback(
1013 IncrementalMapperController::LAST_IMAGE_REG_CALLBACK, [&]() {
1014 // If the number of reconstructions has not changed, the last model
1015 // was discarded for some reason.
1016 if (reconstruction_manager.Size() > prev_num_reconstructions) {
1017 const std::string reconstruction_path = JoinPaths(
1018 output_path, std::to_string(prev_num_reconstructions));
1019 const auto& reconstruction =
1020 reconstruction_manager.Get(prev_num_reconstructions);
1021 CreateDirIfNotExists(reconstruction_path);
1022 reconstruction.Write(reconstruction_path);
1023 options.Write(JoinPaths(reconstruction_path, "project.ini"));
1024 prev_num_reconstructions = reconstruction_manager.Size();
1025 }
1026 });
1027 }
1028
1029 mapper.Start();
1030 mapper.Wait();
1031
1032 // In case the reconstruction is continued from an existing reconstruction, do
1033 // not create sub-folders but directly write the results.
1034 if (input_path != "" && reconstruction_manager.Size() > 0) {
1035 reconstruction_manager.Get(0).Write(output_path);
1036 }
1037
1038 return EXIT_SUCCESS;
1039 }
1040
RunHierarchicalMapper(int argc,char ** argv)1041 int RunHierarchicalMapper(int argc, char** argv) {
1042 HierarchicalMapperController::Options hierarchical_options;
1043 SceneClustering::Options clustering_options;
1044 std::string output_path;
1045
1046 OptionManager options;
1047 options.AddRequiredOption("database_path",
1048 &hierarchical_options.database_path);
1049 options.AddRequiredOption("image_path", &hierarchical_options.image_path);
1050 options.AddRequiredOption("output_path", &output_path);
1051 options.AddDefaultOption("num_workers", &hierarchical_options.num_workers);
1052 options.AddDefaultOption("image_overlap", &clustering_options.image_overlap);
1053 options.AddDefaultOption("leaf_max_num_images",
1054 &clustering_options.leaf_max_num_images);
1055 options.AddMapperOptions();
1056 options.Parse(argc, argv);
1057
1058 if (!ExistsDir(output_path)) {
1059 std::cerr << "ERROR: `output_path` is not a directory." << std::endl;
1060 return EXIT_FAILURE;
1061 }
1062
1063 ReconstructionManager reconstruction_manager;
1064
1065 HierarchicalMapperController hierarchical_mapper(
1066 hierarchical_options, clustering_options, *options.mapper,
1067 &reconstruction_manager);
1068 hierarchical_mapper.Start();
1069 hierarchical_mapper.Wait();
1070
1071 reconstruction_manager.Write(output_path, &options);
1072
1073 return EXIT_SUCCESS;
1074 }
1075
RunMatchesImporter(int argc,char ** argv)1076 int RunMatchesImporter(int argc, char** argv) {
1077 std::string match_list_path;
1078 std::string match_type = "pairs";
1079
1080 OptionManager options;
1081 options.AddDatabaseOptions();
1082 options.AddRequiredOption("match_list_path", &match_list_path);
1083 options.AddDefaultOption("match_type", &match_type,
1084 "{'pairs', 'raw', 'inliers'}");
1085 options.AddMatchingOptions();
1086 options.Parse(argc, argv);
1087
1088 std::unique_ptr<QApplication> app;
1089 if (options.sift_matching->use_gpu && kUseOpenGL) {
1090 app.reset(new QApplication(argc, argv));
1091 }
1092
1093 std::unique_ptr<Thread> feature_matcher;
1094 if (match_type == "pairs") {
1095 ImagePairsMatchingOptions matcher_options;
1096 matcher_options.match_list_path = match_list_path;
1097 feature_matcher.reset(new ImagePairsFeatureMatcher(
1098 matcher_options, *options.sift_matching, *options.database_path));
1099 } else if (match_type == "raw" || match_type == "inliers") {
1100 FeaturePairsMatchingOptions matcher_options;
1101 matcher_options.match_list_path = match_list_path;
1102 matcher_options.verify_matches = match_type == "raw";
1103 feature_matcher.reset(new FeaturePairsFeatureMatcher(
1104 matcher_options, *options.sift_matching, *options.database_path));
1105 } else {
1106 std::cerr << "ERROR: Invalid `match_type`";
1107 return EXIT_FAILURE;
1108 }
1109
1110 if (options.sift_matching->use_gpu && kUseOpenGL) {
1111 RunThreadWithOpenGLContext(feature_matcher.get());
1112 } else {
1113 feature_matcher->Start();
1114 feature_matcher->Wait();
1115 }
1116
1117 return EXIT_SUCCESS;
1118 }
1119
RunModelAligner(int argc,char ** argv)1120 int RunModelAligner(int argc, char** argv) {
1121 std::string input_path;
1122 std::string ref_images_path;
1123 std::string output_path;
1124 int min_common_images = 3;
1125 bool robust_alignment = true;
1126 RANSACOptions ransac_options;
1127
1128 OptionManager options;
1129 options.AddRequiredOption("input_path", &input_path);
1130 options.AddRequiredOption("ref_images_path", &ref_images_path);
1131 options.AddRequiredOption("output_path", &output_path);
1132 options.AddDefaultOption("min_common_images", &min_common_images);
1133 options.AddDefaultOption("robust_alignment", &robust_alignment);
1134 options.AddDefaultOption("robust_alignment_max_error",
1135 &ransac_options.max_error);
1136 options.Parse(argc, argv);
1137
1138 if (robust_alignment && ransac_options.max_error <= 0) {
1139 std::cout << "ERROR: You must provide a maximum alignment error > 0"
1140 << std::endl;
1141 return EXIT_FAILURE;
1142 }
1143
1144 std::vector<std::string> ref_image_names;
1145 std::vector<Eigen::Vector3d> ref_locations;
1146 std::vector<std::string> lines = ReadTextFileLines(ref_images_path);
1147 for (const auto line : lines) {
1148 std::stringstream line_parser(line);
1149 std::string image_name = "";
1150 Eigen::Vector3d camera_position;
1151 line_parser >> image_name >> camera_position[0] >> camera_position[1] >>
1152 camera_position[2];
1153 ref_image_names.push_back(image_name);
1154 ref_locations.push_back(camera_position);
1155 }
1156
1157 Reconstruction reconstruction;
1158 reconstruction.Read(input_path);
1159
1160 PrintHeading2("Aligning reconstruction");
1161
1162 std::cout << StringPrintf(" => Using %d reference images",
1163 ref_image_names.size())
1164 << std::endl;
1165
1166 bool alignment_success;
1167 if (robust_alignment) {
1168 alignment_success = reconstruction.AlignRobust(
1169 ref_image_names, ref_locations, min_common_images, ransac_options);
1170 } else {
1171 alignment_success =
1172 reconstruction.Align(ref_image_names, ref_locations, min_common_images);
1173 }
1174
1175 if (alignment_success) {
1176 std::cout << " => Alignment succeeded" << std::endl;
1177 reconstruction.Write(output_path);
1178
1179 std::vector<double> errors;
1180 errors.reserve(ref_image_names.size());
1181
1182 for (size_t i = 0; i < ref_image_names.size(); ++i) {
1183 const Image* image = reconstruction.FindImageWithName(ref_image_names[i]);
1184 if (image != nullptr) {
1185 errors.push_back((image->ProjectionCenter() - ref_locations[i]).norm());
1186 }
1187 }
1188
1189 std::cout << StringPrintf(" => Alignment error: %f (mean), %f (median)",
1190 Mean(errors), Median(errors))
1191 << std::endl;
1192 } else {
1193 std::cout << " => Alignment failed" << std::endl;
1194 }
1195
1196 return EXIT_SUCCESS;
1197 }
1198
RunModelAnalyzer(int argc,char ** argv)1199 int RunModelAnalyzer(int argc, char** argv) {
1200 std::string path;
1201
1202 OptionManager options;
1203 options.AddRequiredOption("path", &path);
1204 options.Parse(argc, argv);
1205
1206 Reconstruction reconstruction;
1207 reconstruction.Read(path);
1208
1209 std::cout << StringPrintf("Cameras: %d", reconstruction.NumCameras())
1210 << std::endl;
1211 std::cout << StringPrintf("Images: %d", reconstruction.NumImages())
1212 << std::endl;
1213 std::cout << StringPrintf("Registered images: %d",
1214 reconstruction.NumRegImages())
1215 << std::endl;
1216 std::cout << StringPrintf("Points: %d", reconstruction.NumPoints3D())
1217 << std::endl;
1218 std::cout << StringPrintf("Observations: %d",
1219 reconstruction.ComputeNumObservations())
1220 << std::endl;
1221 std::cout << StringPrintf("Mean track length: %f",
1222 reconstruction.ComputeMeanTrackLength())
1223 << std::endl;
1224 std::cout << StringPrintf("Mean observations per image: %f",
1225 reconstruction.ComputeMeanObservationsPerRegImage())
1226 << std::endl;
1227 std::cout << StringPrintf("Mean reprojection error: %fpx",
1228 reconstruction.ComputeMeanReprojectionError())
1229 << std::endl;
1230
1231 return EXIT_SUCCESS;
1232 }
1233
RunModelConverter(int argc,char ** argv)1234 int RunModelConverter(int argc, char** argv) {
1235 std::string input_path;
1236 std::string output_path;
1237 std::string output_type;
1238
1239 OptionManager options;
1240 options.AddRequiredOption("input_path", &input_path);
1241 options.AddRequiredOption("output_path", &output_path);
1242 options.AddRequiredOption("output_type", &output_type,
1243 "{BIN, TXT, NVM, Bundler, VRML, PLY}");
1244 options.Parse(argc, argv);
1245
1246 Reconstruction reconstruction;
1247 reconstruction.Read(input_path);
1248
1249 StringToLower(&output_type);
1250 if (output_type == "bin") {
1251 reconstruction.WriteBinary(output_path);
1252 } else if (output_type == "txt") {
1253 reconstruction.WriteText(output_path);
1254 } else if (output_type == "nvm") {
1255 reconstruction.ExportNVM(output_path);
1256 } else if (output_type == "bundler") {
1257 reconstruction.ExportBundler(output_path + ".bundle.out",
1258 output_path + ".list.txt");
1259 } else if (output_type == "ply") {
1260 reconstruction.ExportPLY(output_path);
1261 } else if (output_type == "vrml") {
1262 const auto base_path = output_path.substr(0, output_path.find_last_of("."));
1263 reconstruction.ExportVRML(base_path + ".images.wrl",
1264 base_path + ".points3D.wrl", 1,
1265 Eigen::Vector3d(1, 0, 0));
1266 } else {
1267 std::cerr << "ERROR: Invalid `output_type`" << std::endl;
1268 return EXIT_FAILURE;
1269 }
1270
1271 return EXIT_SUCCESS;
1272 }
1273
RunModelMerger(int argc,char ** argv)1274 int RunModelMerger(int argc, char** argv) {
1275 std::string input_path1;
1276 std::string input_path2;
1277 std::string output_path;
1278 double max_reproj_error = 64.0;
1279
1280 OptionManager options;
1281 options.AddRequiredOption("input_path1", &input_path1);
1282 options.AddRequiredOption("input_path2", &input_path2);
1283 options.AddRequiredOption("output_path", &output_path);
1284 options.AddDefaultOption("max_reproj_error", &max_reproj_error);
1285 options.Parse(argc, argv);
1286
1287 Reconstruction reconstruction1;
1288 reconstruction1.Read(input_path1);
1289 PrintHeading2("Reconstruction 1");
1290 std::cout << StringPrintf("Images: %d", reconstruction1.NumRegImages())
1291 << std::endl;
1292 std::cout << StringPrintf("Points: %d", reconstruction1.NumPoints3D())
1293 << std::endl;
1294
1295 Reconstruction reconstruction2;
1296 reconstruction2.Read(input_path2);
1297 PrintHeading2("Reconstruction 2");
1298 std::cout << StringPrintf("Images: %d", reconstruction2.NumRegImages())
1299 << std::endl;
1300 std::cout << StringPrintf("Points: %d", reconstruction2.NumPoints3D())
1301 << std::endl;
1302
1303 PrintHeading2("Merging reconstructions");
1304 if (reconstruction1.Merge(reconstruction2, max_reproj_error)) {
1305 std::cout << "=> Merge succeeded" << std::endl;
1306 PrintHeading2("Merged reconstruction");
1307 std::cout << StringPrintf("Images: %d", reconstruction1.NumRegImages())
1308 << std::endl;
1309 std::cout << StringPrintf("Points: %d", reconstruction1.NumPoints3D())
1310 << std::endl;
1311 } else {
1312 std::cout << "=> Merge failed" << std::endl;
1313 }
1314
1315 reconstruction1.Write(output_path);
1316
1317 return EXIT_SUCCESS;
1318 }
1319
RunModelOrientationAligner(int argc,char ** argv)1320 int RunModelOrientationAligner(int argc, char** argv) {
1321 std::string input_path;
1322 std::string output_path;
1323 std::string method = "MANHATTAN-WORLD";
1324
1325 ManhattanWorldFrameEstimationOptions frame_estimation_options;
1326
1327 OptionManager options;
1328 options.AddImageOptions();
1329 options.AddRequiredOption("input_path", &input_path);
1330 options.AddRequiredOption("output_path", &output_path);
1331 options.AddDefaultOption("method", &method,
1332 "{MANHATTAN-WORLD, IMAGE-ORIENTATION}");
1333 options.AddDefaultOption("max_image_size",
1334 &frame_estimation_options.max_image_size);
1335 options.Parse(argc, argv);
1336
1337 StringToLower(&method);
1338 if (method != "manhattan-world" && method != "image-orientation") {
1339 std::cout << "ERROR: Invalid `method` - supported values are "
1340 "'MANHATTAN-WORLD' or 'IMAGE-ORIENTATION'."
1341 << std::endl;
1342 return EXIT_FAILURE;
1343 }
1344
1345 Reconstruction reconstruction;
1346 reconstruction.Read(input_path);
1347
1348 PrintHeading1("Aligning Reconstruction");
1349
1350 Eigen::Matrix3d tform;
1351
1352 if (method == "manhattan-world") {
1353 const Eigen::Matrix3d frame = EstimateManhattanWorldFrame(
1354 frame_estimation_options, reconstruction, *options.image_path);
1355
1356 if (frame.col(0).nonZeros() == 0) {
1357 std::cout << "Only aligning vertical axis" << std::endl;
1358 tform = RotationFromUnitVectors(frame.col(1), Eigen::Vector3d(0, 1, 0));
1359 } else if (frame.col(1).nonZeros() == 0) {
1360 tform = RotationFromUnitVectors(frame.col(0), Eigen::Vector3d(1, 0, 0));
1361 std::cout << "Only aligning horizontal axis" << std::endl;
1362 } else {
1363 tform = frame.transpose();
1364 std::cout << "Aligning horizontal and vertical axes" << std::endl;
1365 }
1366 } else if (method == "image-orientation") {
1367 const Eigen::Vector3d gravity_axis =
1368 EstimateGravityVectorFromImageOrientation(reconstruction);
1369 tform = RotationFromUnitVectors(gravity_axis, Eigen::Vector3d(0, 1, 0));
1370 } else {
1371 LOG(FATAL) << "Alignment method not supported";
1372 }
1373
1374 std::cout << "Using the rotation matrix:" << std::endl;
1375 std::cout << tform << std::endl;
1376
1377 reconstruction.Transform(SimilarityTransform3(
1378 1, RotationMatrixToQuaternion(tform), Eigen::Vector3d(0, 0, 0)));
1379
1380 std::cout << "Writing aligned reconstruction..." << std::endl;
1381 reconstruction.Write(output_path);
1382
1383 return EXIT_SUCCESS;
1384 }
1385
RunSequentialMatcher(int argc,char ** argv)1386 int RunSequentialMatcher(int argc, char** argv) {
1387 OptionManager options;
1388 options.AddDatabaseOptions();
1389 options.AddSequentialMatchingOptions();
1390 options.Parse(argc, argv);
1391
1392 std::unique_ptr<QApplication> app;
1393 if (options.sift_matching->use_gpu && kUseOpenGL) {
1394 app.reset(new QApplication(argc, argv));
1395 }
1396
1397 SequentialFeatureMatcher feature_matcher(*options.sequential_matching,
1398 *options.sift_matching,
1399 *options.database_path);
1400
1401 if (options.sift_matching->use_gpu && kUseOpenGL) {
1402 RunThreadWithOpenGLContext(&feature_matcher);
1403 } else {
1404 feature_matcher.Start();
1405 feature_matcher.Wait();
1406 }
1407
1408 return EXIT_SUCCESS;
1409 }
1410
RunPointFiltering(int argc,char ** argv)1411 int RunPointFiltering(int argc, char** argv) {
1412 std::string input_path;
1413 std::string output_path;
1414
1415 size_t min_track_len = 2;
1416 double max_reproj_error = 4.0;
1417 double min_tri_angle = 1.5;
1418
1419 OptionManager options;
1420 options.AddRequiredOption("input_path", &input_path);
1421 options.AddRequiredOption("output_path", &output_path);
1422 options.AddDefaultOption("min_track_len", &min_track_len);
1423 options.AddDefaultOption("max_reproj_error", &max_reproj_error);
1424 options.AddDefaultOption("min_tri_angle", &min_tri_angle);
1425 options.Parse(argc, argv);
1426
1427 Reconstruction reconstruction;
1428 reconstruction.Read(input_path);
1429
1430 size_t num_filtered =
1431 reconstruction.FilterAllPoints3D(max_reproj_error, min_tri_angle);
1432
1433 for (const auto point3D_id : reconstruction.Point3DIds()) {
1434 const auto& point3D = reconstruction.Point3D(point3D_id);
1435 if (point3D.Track().Length() < min_track_len) {
1436 num_filtered += point3D.Track().Length();
1437 reconstruction.DeletePoint3D(point3D_id);
1438 }
1439 }
1440
1441 std::cout << "Filtered observations: " << num_filtered << std::endl;
1442
1443 reconstruction.Write(output_path);
1444
1445 return EXIT_SUCCESS;
1446 }
1447
RunPointTriangulator(int argc,char ** argv)1448 int RunPointTriangulator(int argc, char** argv) {
1449 std::string input_path;
1450 std::string output_path;
1451 bool clear_points = false;
1452
1453 OptionManager options;
1454 options.AddDatabaseOptions();
1455 options.AddImageOptions();
1456 options.AddRequiredOption("input_path", &input_path);
1457 options.AddRequiredOption("output_path", &output_path);
1458 options.AddDefaultOption(
1459 "clear_points", &clear_points,
1460 "Whether to clear all existing points and observations");
1461 options.AddMapperOptions();
1462 options.Parse(argc, argv);
1463
1464 if (!ExistsDir(input_path)) {
1465 std::cerr << "ERROR: `input_path` is not a directory" << std::endl;
1466 return EXIT_FAILURE;
1467 }
1468
1469 if (!ExistsDir(output_path)) {
1470 std::cerr << "ERROR: `output_path` is not a directory" << std::endl;
1471 return EXIT_FAILURE;
1472 }
1473
1474 const auto& mapper_options = *options.mapper;
1475
1476 PrintHeading1("Loading model");
1477
1478 Reconstruction reconstruction;
1479 reconstruction.Read(input_path);
1480
1481 PrintHeading1("Loading database");
1482
1483 DatabaseCache database_cache;
1484
1485 {
1486 Timer timer;
1487 timer.Start();
1488
1489 Database database(*options.database_path);
1490
1491 const size_t min_num_matches =
1492 static_cast<size_t>(mapper_options.min_num_matches);
1493 database_cache.Load(database, min_num_matches,
1494 mapper_options.ignore_watermarks,
1495 mapper_options.image_names);
1496
1497 if (clear_points) {
1498 reconstruction.DeleteAllPoints2DAndPoints3D();
1499 reconstruction.TranscribeImageIdsToDatabase(database);
1500 }
1501
1502 std::cout << std::endl;
1503 timer.PrintMinutes();
1504 }
1505
1506 std::cout << std::endl;
1507
1508 CHECK_GE(reconstruction.NumRegImages(), 2)
1509 << "Need at least two images for triangulation";
1510
1511 IncrementalMapper mapper(&database_cache);
1512 mapper.BeginReconstruction(&reconstruction);
1513
1514 //////////////////////////////////////////////////////////////////////////////
1515 // Triangulation
1516 //////////////////////////////////////////////////////////////////////////////
1517
1518 const auto tri_options = mapper_options.Triangulation();
1519
1520 const auto& reg_image_ids = reconstruction.RegImageIds();
1521
1522 for (size_t i = 0; i < reg_image_ids.size(); ++i) {
1523 const image_t image_id = reg_image_ids[i];
1524
1525 const auto& image = reconstruction.Image(image_id);
1526
1527 PrintHeading1(StringPrintf("Triangulating image #%d (%d)", image_id, i));
1528
1529 const size_t num_existing_points3D = image.NumPoints3D();
1530
1531 std::cout << " => Image sees " << num_existing_points3D << " / "
1532 << image.NumObservations() << " points" << std::endl;
1533
1534 mapper.TriangulateImage(tri_options, image_id);
1535
1536 std::cout << " => Triangulated "
1537 << (image.NumPoints3D() - num_existing_points3D) << " points"
1538 << std::endl;
1539 }
1540
1541 //////////////////////////////////////////////////////////////////////////////
1542 // Retriangulation
1543 //////////////////////////////////////////////////////////////////////////////
1544
1545 PrintHeading1("Retriangulation");
1546
1547 CompleteAndMergeTracks(mapper_options, &mapper);
1548
1549 //////////////////////////////////////////////////////////////////////////////
1550 // Bundle adjustment
1551 //////////////////////////////////////////////////////////////////////////////
1552
1553 auto ba_options = mapper_options.GlobalBundleAdjustment();
1554 ba_options.refine_focal_length = false;
1555 ba_options.refine_principal_point = false;
1556 ba_options.refine_extra_params = false;
1557 ba_options.refine_extrinsics = false;
1558
1559 // Configure bundle adjustment.
1560 BundleAdjustmentConfig ba_config;
1561 for (const image_t image_id : reconstruction.RegImageIds()) {
1562 ba_config.AddImage(image_id);
1563 }
1564
1565 for (int i = 0; i < mapper_options.ba_global_max_refinements; ++i) {
1566 // Avoid degeneracies in bundle adjustment.
1567 reconstruction.FilterObservationsWithNegativeDepth();
1568
1569 const size_t num_observations = reconstruction.ComputeNumObservations();
1570
1571 PrintHeading1("Bundle adjustment");
1572 BundleAdjuster bundle_adjuster(ba_options, ba_config);
1573 CHECK(bundle_adjuster.Solve(&reconstruction));
1574
1575 size_t num_changed_observations = 0;
1576 num_changed_observations += CompleteAndMergeTracks(mapper_options, &mapper);
1577 num_changed_observations += FilterPoints(mapper_options, &mapper);
1578 const double changed =
1579 static_cast<double>(num_changed_observations) / num_observations;
1580 std::cout << StringPrintf(" => Changed observations: %.6f", changed)
1581 << std::endl;
1582 if (changed < mapper_options.ba_global_max_refinement_change) {
1583 break;
1584 }
1585 }
1586
1587 PrintHeading1("Extracting colors");
1588 reconstruction.ExtractColorsForAllImages(*options.image_path);
1589
1590 const bool kDiscardReconstruction = false;
1591 mapper.EndReconstruction(kDiscardReconstruction);
1592
1593 reconstruction.Write(output_path);
1594
1595 return EXIT_SUCCESS;
1596 }
1597
1598 // Read the configuration of the camera rigs from a JSON file. The input images
1599 // of a camera rig must be named consistently to assign them to the appropriate
1600 // camera rig and the respective snapshots.
1601 //
1602 // An example configuration of a single camera rig:
1603 // [
1604 // {
1605 // "ref_camera_id": 1,
1606 // "cameras":
1607 // [
1608 // {
1609 // "camera_id": 1,
1610 // "image_prefix": "left1_image"
1611 // },
1612 // {
1613 // "camera_id": 2,
1614 // "image_prefix": "left2_image"
1615 // },
1616 // {
1617 // "camera_id": 3,
1618 // "image_prefix": "right1_image"
1619 // },
1620 // {
1621 // "camera_id": 4,
1622 // "image_prefix": "right2_image"
1623 // }
1624 // ]
1625 // }
1626 // ]
1627 //
1628 // This file specifies the configuration for a single camera rig and that you
1629 // could potentially define multiple camera rigs. The rig is composed of 4
1630 // cameras: all images of the first camera must have "left1_image" as a name
1631 // prefix, e.g., "left1_image_frame000.png" or "left1_image/frame000.png".
1632 // Images with the same suffix ("_frame000.png" and "/frame000.png") are
1633 // assigned to the same snapshot, i.e., they are assumed to be captured at the
1634 // same time. Only snapshots with the reference image registered will be added
1635 // to the bundle adjustment problem. The remaining images will be added with
1636 // independent poses to the bundle adjustment problem. The above configuration
1637 // could have the following input image file structure:
1638 //
1639 // /path/to/images/...
1640 // left1_image/...
1641 // frame000.png
1642 // frame001.png
1643 // frame002.png
1644 // ...
1645 // left2_image/...
1646 // frame000.png
1647 // frame001.png
1648 // frame002.png
1649 // ...
1650 // right1_image/...
1651 // frame000.png
1652 // frame001.png
1653 // frame002.png
1654 // ...
1655 // right2_image/...
1656 // frame000.png
1657 // frame001.png
1658 // frame002.png
1659 // ...
1660 //
1661 // TODO: Provide an option to manually / explicitly set the relative extrinsics
1662 // of the camera rig. At the moment, the relative extrinsics are automatically
1663 // inferred from the reconstruction.
ReadCameraRigConfig(const std::string & rig_config_path,const Reconstruction & reconstruction)1664 std::vector<CameraRig> ReadCameraRigConfig(
1665 const std::string& rig_config_path, const Reconstruction& reconstruction) {
1666 boost::property_tree::ptree pt;
1667 boost::property_tree::read_json(rig_config_path.c_str(), pt);
1668
1669 std::vector<CameraRig> camera_rigs;
1670 for (const auto& rig_config : pt) {
1671 CameraRig camera_rig;
1672
1673 std::vector<std::string> image_prefixes;
1674 for (const auto& camera : rig_config.second.get_child("cameras")) {
1675 const int camera_id = camera.second.get<int>("camera_id");
1676 image_prefixes.push_back(camera.second.get<std::string>("image_prefix"));
1677 camera_rig.AddCamera(camera_id, ComposeIdentityQuaternion(),
1678 Eigen::Vector3d(0, 0, 0));
1679 }
1680
1681 camera_rig.SetRefCameraId(rig_config.second.get<int>("ref_camera_id"));
1682
1683 std::unordered_map<std::string, std::vector<image_t>> snapshots;
1684 for (const auto image_id : reconstruction.RegImageIds()) {
1685 const auto& image = reconstruction.Image(image_id);
1686 for (const auto& image_prefix : image_prefixes) {
1687 if (StringContains(image.Name(), image_prefix)) {
1688 const std::string image_suffix =
1689 StringGetAfter(image.Name(), image_prefix);
1690 snapshots[image_suffix].push_back(image_id);
1691 }
1692 }
1693 }
1694
1695 for (const auto& snapshot : snapshots) {
1696 bool has_ref_camera = false;
1697 for (const auto image_id : snapshot.second) {
1698 const auto& image = reconstruction.Image(image_id);
1699 if (image.CameraId() == camera_rig.RefCameraId()) {
1700 has_ref_camera = true;
1701 }
1702 }
1703
1704 if (has_ref_camera) {
1705 camera_rig.AddSnapshot(snapshot.second);
1706 }
1707 }
1708
1709 camera_rig.Check(reconstruction);
1710 camera_rig.ComputeRelativePoses(reconstruction);
1711
1712 camera_rigs.push_back(camera_rig);
1713 }
1714
1715 return camera_rigs;
1716 }
1717
RunRigBundleAdjuster(int argc,char ** argv)1718 int RunRigBundleAdjuster(int argc, char** argv) {
1719 std::string input_path;
1720 std::string output_path;
1721 std::string rig_config_path;
1722
1723 RigBundleAdjuster::Options rig_ba_options;
1724
1725 OptionManager options;
1726 options.AddRequiredOption("input_path", &input_path);
1727 options.AddRequiredOption("output_path", &output_path);
1728 options.AddRequiredOption("rig_config_path", &rig_config_path);
1729 options.AddDefaultOption("RigBundleAdjustment.refine_relative_poses",
1730 &rig_ba_options.refine_relative_poses);
1731 options.AddBundleAdjustmentOptions();
1732 options.Parse(argc, argv);
1733
1734 Reconstruction reconstruction;
1735 reconstruction.Read(input_path);
1736
1737 PrintHeading1("Camera rig configuration");
1738
1739 auto camera_rigs = ReadCameraRigConfig(rig_config_path, reconstruction);
1740
1741 BundleAdjustmentConfig config;
1742 for (size_t i = 0; i < camera_rigs.size(); ++i) {
1743 const auto& camera_rig = camera_rigs[i];
1744 PrintHeading2(StringPrintf("Camera Rig %d", i + 1));
1745 std::cout << StringPrintf("Cameras: %d", camera_rig.NumCameras())
1746 << std::endl;
1747 std::cout << StringPrintf("Snapshots: %d", camera_rig.NumSnapshots())
1748 << std::endl;
1749
1750 // Add all registered images to the bundle adjustment configuration.
1751 for (const auto image_id : reconstruction.RegImageIds()) {
1752 config.AddImage(image_id);
1753 }
1754 }
1755
1756 PrintHeading1("Rig bundle adjustment");
1757
1758 BundleAdjustmentOptions ba_options = *options.bundle_adjustment;
1759 ba_options.solver_options.minimizer_progress_to_stdout = true;
1760 RigBundleAdjuster bundle_adjuster(ba_options, rig_ba_options, config);
1761 CHECK(bundle_adjuster.Solve(&reconstruction, &camera_rigs));
1762
1763 reconstruction.Write(output_path);
1764
1765 return EXIT_SUCCESS;
1766 }
1767
RunSpatialMatcher(int argc,char ** argv)1768 int RunSpatialMatcher(int argc, char** argv) {
1769 OptionManager options;
1770 options.AddDatabaseOptions();
1771 options.AddSpatialMatchingOptions();
1772 options.Parse(argc, argv);
1773
1774 std::unique_ptr<QApplication> app;
1775 if (options.sift_matching->use_gpu && kUseOpenGL) {
1776 app.reset(new QApplication(argc, argv));
1777 }
1778
1779 SpatialFeatureMatcher feature_matcher(*options.spatial_matching,
1780 *options.sift_matching,
1781 *options.database_path);
1782
1783 if (options.sift_matching->use_gpu && kUseOpenGL) {
1784 RunThreadWithOpenGLContext(&feature_matcher);
1785 } else {
1786 feature_matcher.Start();
1787 feature_matcher.Wait();
1788 }
1789
1790 return EXIT_SUCCESS;
1791 }
1792
RunTransitiveMatcher(int argc,char ** argv)1793 int RunTransitiveMatcher(int argc, char** argv) {
1794 OptionManager options;
1795 options.AddDatabaseOptions();
1796 options.AddTransitiveMatchingOptions();
1797 options.Parse(argc, argv);
1798
1799 std::unique_ptr<QApplication> app;
1800 if (options.sift_matching->use_gpu && kUseOpenGL) {
1801 app.reset(new QApplication(argc, argv));
1802 }
1803
1804 TransitiveFeatureMatcher feature_matcher(*options.transitive_matching,
1805 *options.sift_matching,
1806 *options.database_path);
1807
1808 if (options.sift_matching->use_gpu && kUseOpenGL) {
1809 RunThreadWithOpenGLContext(&feature_matcher);
1810 } else {
1811 feature_matcher.Start();
1812 feature_matcher.Wait();
1813 }
1814
1815 return EXIT_SUCCESS;
1816 }
1817
1818 // Loads descriptors for training from the database. Loads all descriptors from
1819 // the database if max_num_images < 0, otherwise the descriptors of a random
1820 // subset of images are selected.
LoadRandomDatabaseDescriptors(const std::string & database_path,const int max_num_images)1821 FeatureDescriptors LoadRandomDatabaseDescriptors(
1822 const std::string& database_path, const int max_num_images) {
1823 Database database(database_path);
1824 DatabaseTransaction database_transaction(&database);
1825
1826 const std::vector<Image> images = database.ReadAllImages();
1827
1828 FeatureDescriptors descriptors;
1829
1830 std::vector<size_t> image_idxs;
1831 size_t num_descriptors = 0;
1832 if (max_num_images < 0) {
1833 // All images in the database.
1834 image_idxs.resize(images.size());
1835 std::iota(image_idxs.begin(), image_idxs.end(), 0);
1836 num_descriptors = database.NumDescriptors();
1837 } else {
1838 // Random subset of images in the database.
1839 CHECK_LE(max_num_images, images.size());
1840 RandomSampler random_sampler(max_num_images);
1841 random_sampler.Initialize(images.size());
1842 image_idxs = random_sampler.Sample();
1843 for (const auto image_idx : image_idxs) {
1844 const auto& image = images.at(image_idx);
1845 num_descriptors += database.NumDescriptorsForImage(image.ImageId());
1846 }
1847 }
1848
1849 descriptors.resize(num_descriptors, 128);
1850
1851 size_t descriptor_row = 0;
1852 for (const auto image_idx : image_idxs) {
1853 const auto& image = images.at(image_idx);
1854 const FeatureDescriptors image_descriptors =
1855 database.ReadDescriptors(image.ImageId());
1856 descriptors.block(descriptor_row, 0, image_descriptors.rows(), 128) =
1857 image_descriptors;
1858 descriptor_row += image_descriptors.rows();
1859 }
1860
1861 CHECK_EQ(descriptor_row, num_descriptors);
1862
1863 return descriptors;
1864 }
1865
RunVocabTreeBuilder(int argc,char ** argv)1866 int RunVocabTreeBuilder(int argc, char** argv) {
1867 std::string vocab_tree_path;
1868 retrieval::VisualIndex<>::BuildOptions build_options;
1869 int max_num_images = -1;
1870
1871 OptionManager options;
1872 options.AddDatabaseOptions();
1873 options.AddRequiredOption("vocab_tree_path", &vocab_tree_path);
1874 options.AddDefaultOption("num_visual_words", &build_options.num_visual_words);
1875 options.AddDefaultOption("num_checks", &build_options.num_checks);
1876 options.AddDefaultOption("branching", &build_options.branching);
1877 options.AddDefaultOption("num_iterations", &build_options.num_iterations);
1878 options.AddDefaultOption("max_num_images", &max_num_images);
1879 options.Parse(argc, argv);
1880
1881 retrieval::VisualIndex<> visual_index;
1882
1883 std::cout << "Loading descriptors..." << std::endl;
1884 const auto descriptors =
1885 LoadRandomDatabaseDescriptors(*options.database_path, max_num_images);
1886 std::cout << " => Loaded a total of " << descriptors.rows() << " descriptors"
1887 << std::endl;
1888
1889 std::cout << "Building index for visual words..." << std::endl;
1890 visual_index.Build(build_options, descriptors);
1891 std::cout << " => Quantized descriptor space using "
1892 << visual_index.NumVisualWords() << " visual words" << std::endl;
1893
1894 std::cout << "Saving index to file..." << std::endl;
1895 visual_index.Write(vocab_tree_path);
1896
1897 return EXIT_SUCCESS;
1898 }
1899
RunVocabTreeMatcher(int argc,char ** argv)1900 int RunVocabTreeMatcher(int argc, char** argv) {
1901 OptionManager options;
1902 options.AddDatabaseOptions();
1903 options.AddVocabTreeMatchingOptions();
1904 options.Parse(argc, argv);
1905
1906 std::unique_ptr<QApplication> app;
1907 if (options.sift_matching->use_gpu && kUseOpenGL) {
1908 app.reset(new QApplication(argc, argv));
1909 }
1910
1911 VocabTreeFeatureMatcher feature_matcher(*options.vocab_tree_matching,
1912 *options.sift_matching,
1913 *options.database_path);
1914
1915 if (options.sift_matching->use_gpu && kUseOpenGL) {
1916 RunThreadWithOpenGLContext(&feature_matcher);
1917 } else {
1918 feature_matcher.Start();
1919 feature_matcher.Wait();
1920 }
1921
1922 return EXIT_SUCCESS;
1923 }
1924
ReadVocabTreeRetrievalImageList(const std::string & path,Database * database)1925 std::vector<Image> ReadVocabTreeRetrievalImageList(const std::string& path,
1926 Database* database) {
1927 std::vector<Image> images;
1928 if (path.empty()) {
1929 images.reserve(database->NumImages());
1930 for (const auto& image : database->ReadAllImages()) {
1931 images.push_back(image);
1932 }
1933 } else {
1934 DatabaseTransaction database_transaction(database);
1935
1936 const auto image_names = ReadTextFileLines(path);
1937 images.reserve(image_names.size());
1938 for (const auto& image_name : image_names) {
1939 const auto image = database->ReadImageWithName(image_name);
1940 CHECK_NE(image.ImageId(), kInvalidImageId);
1941 images.push_back(image);
1942 }
1943 }
1944 return images;
1945 }
1946
RunVocabTreeRetriever(int argc,char ** argv)1947 int RunVocabTreeRetriever(int argc, char** argv) {
1948 std::string vocab_tree_path;
1949 std::string database_image_list_path;
1950 std::string query_image_list_path;
1951 std::string output_index_path;
1952 retrieval::VisualIndex<>::QueryOptions query_options;
1953 int max_num_features = -1;
1954
1955 OptionManager options;
1956 options.AddDatabaseOptions();
1957 options.AddRequiredOption("vocab_tree_path", &vocab_tree_path);
1958 options.AddDefaultOption("database_image_list_path",
1959 &database_image_list_path);
1960 options.AddDefaultOption("query_image_list_path", &query_image_list_path);
1961 options.AddDefaultOption("output_index_path", &output_index_path);
1962 options.AddDefaultOption("num_images", &query_options.max_num_images);
1963 options.AddDefaultOption("num_neighbors", &query_options.num_neighbors);
1964 options.AddDefaultOption("num_checks", &query_options.num_checks);
1965 options.AddDefaultOption("num_images_after_verification",
1966 &query_options.num_images_after_verification);
1967 options.AddDefaultOption("max_num_features", &max_num_features);
1968 options.Parse(argc, argv);
1969
1970 retrieval::VisualIndex<> visual_index;
1971 visual_index.Read(vocab_tree_path);
1972
1973 Database database(*options.database_path);
1974
1975 const auto database_images =
1976 ReadVocabTreeRetrievalImageList(database_image_list_path, &database);
1977 const auto query_images =
1978 (!query_image_list_path.empty() || output_index_path.empty())
1979 ? ReadVocabTreeRetrievalImageList(query_image_list_path, &database)
1980 : std::vector<Image>();
1981
1982 //////////////////////////////////////////////////////////////////////////////
1983 // Perform image indexing
1984 //////////////////////////////////////////////////////////////////////////////
1985
1986 for (size_t i = 0; i < database_images.size(); ++i) {
1987 Timer timer;
1988 timer.Start();
1989
1990 std::cout << StringPrintf("Indexing image [%d/%d]", i + 1,
1991 database_images.size())
1992 << std::flush;
1993
1994 if (visual_index.ImageIndexed(database_images[i].ImageId())) {
1995 std::cout << std::endl;
1996 continue;
1997 }
1998
1999 auto keypoints = database.ReadKeypoints(database_images[i].ImageId());
2000 auto descriptors = database.ReadDescriptors(database_images[i].ImageId());
2001 if (max_num_features > 0 && descriptors.rows() > max_num_features) {
2002 ExtractTopScaleFeatures(&keypoints, &descriptors, max_num_features);
2003 }
2004
2005 visual_index.Add(retrieval::VisualIndex<>::IndexOptions(),
2006 database_images[i].ImageId(), keypoints, descriptors);
2007
2008 std::cout << StringPrintf(" in %.3fs", timer.ElapsedSeconds()) << std::endl;
2009 }
2010
2011 // Compute the TF-IDF weights, etc.
2012 visual_index.Prepare();
2013
2014 // Optionally save the indexing data for the database images (as well as the
2015 // original vocabulary tree data) to speed up future indexing.
2016 if (!output_index_path.empty()) {
2017 visual_index.Write(output_index_path);
2018 }
2019
2020 if (query_images.empty()) {
2021 return EXIT_SUCCESS;
2022 }
2023
2024 //////////////////////////////////////////////////////////////////////////////
2025 // Perform image queries
2026 //////////////////////////////////////////////////////////////////////////////
2027
2028 std::unordered_map<image_t, const Image*> image_id_to_image;
2029 image_id_to_image.reserve(database_images.size());
2030 for (const auto& image : database_images) {
2031 image_id_to_image.emplace(image.ImageId(), &image);
2032 }
2033
2034 for (size_t i = 0; i < query_images.size(); ++i) {
2035 Timer timer;
2036 timer.Start();
2037
2038 std::cout << StringPrintf("Querying for image %s [%d/%d]",
2039 query_images[i].Name().c_str(), i + 1,
2040 query_images.size())
2041 << std::flush;
2042
2043 auto keypoints = database.ReadKeypoints(query_images[i].ImageId());
2044 auto descriptors = database.ReadDescriptors(query_images[i].ImageId());
2045 if (max_num_features > 0 && descriptors.rows() > max_num_features) {
2046 ExtractTopScaleFeatures(&keypoints, &descriptors, max_num_features);
2047 }
2048
2049 std::vector<retrieval::ImageScore> image_scores;
2050 visual_index.Query(query_options, keypoints, descriptors, &image_scores);
2051
2052 std::cout << StringPrintf(" in %.3fs", timer.ElapsedSeconds()) << std::endl;
2053 for (const auto& image_score : image_scores) {
2054 const auto& image = *image_id_to_image.at(image_score.image_id);
2055 std::cout << StringPrintf(" image_id=%d, image_name=%s, score=%f",
2056 image_score.image_id, image.Name().c_str(),
2057 image_score.score)
2058 << std::endl;
2059 }
2060 }
2061
2062 return EXIT_SUCCESS;
2063 }
2064
2065 typedef std::function<int(int, char**)> command_func_t;
2066
ShowHelp(const std::vector<std::pair<std::string,command_func_t>> & commands)2067 int ShowHelp(
2068 const std::vector<std::pair<std::string, command_func_t>>& commands) {
2069 std::cout << StringPrintf(
2070 "%s -- Structure-from-Motion and Multi-View Stereo\n"
2071 " (%s)",
2072 GetVersionInfo().c_str(), GetBuildInfo().c_str())
2073 << std::endl
2074 << std::endl;
2075
2076 std::cout << "Usage:" << std::endl;
2077 std::cout << " colmap [command] [options]" << std::endl << std::endl;
2078
2079 std::cout << "Documentation:" << std::endl;
2080 std::cout << " https://colmap.github.io/" << std::endl << std::endl;
2081
2082 std::cout << "Example usage:" << std::endl;
2083 std::cout << " colmap help [ -h, --help ]" << std::endl;
2084 std::cout << " colmap gui" << std::endl;
2085 std::cout << " colmap gui -h [ --help ]" << std::endl;
2086 std::cout << " colmap automatic_reconstructor -h [ --help ]" << std::endl;
2087 std::cout << " colmap automatic_reconstructor --image_path IMAGES "
2088 "--workspace_path WORKSPACE"
2089 << std::endl;
2090 std::cout << " colmap feature_extractor --image_path IMAGES --database_path "
2091 "DATABASE"
2092 << std::endl;
2093 std::cout << " colmap exhaustive_matcher --database_path DATABASE"
2094 << std::endl;
2095 std::cout << " colmap mapper --image_path IMAGES --database_path DATABASE "
2096 "--output_path MODEL"
2097 << std::endl;
2098 std::cout << " ..." << std::endl << std::endl;
2099
2100 std::cout << "Available commands:" << std::endl;
2101 std::cout << " help" << std::endl;
2102 for (const auto& command : commands) {
2103 std::cout << " " << command.first << std::endl;
2104 }
2105 std::cout << std::endl;
2106
2107 return EXIT_SUCCESS;
2108 }
2109
main(int argc,char ** argv)2110 int main(int argc, char** argv) {
2111 InitializeGlog(argv);
2112
2113 std::vector<std::pair<std::string, command_func_t>> commands;
2114 commands.emplace_back("gui", &RunGraphicalUserInterface);
2115 commands.emplace_back("automatic_reconstructor", &RunAutomaticReconstructor);
2116 commands.emplace_back("bundle_adjuster", &RunBundleAdjuster);
2117 commands.emplace_back("color_extractor", &RunColorExtractor);
2118 commands.emplace_back("database_creator", &RunDatabaseCreator);
2119 commands.emplace_back("database_merger", &RunDatabaseMerger);
2120 commands.emplace_back("delaunay_mesher", &RunDelaunayMesher);
2121 commands.emplace_back("exhaustive_matcher", &RunExhaustiveMatcher);
2122 commands.emplace_back("feature_extractor", &RunFeatureExtractor);
2123 commands.emplace_back("feature_importer", &RunFeatureImporter);
2124 commands.emplace_back("hierarchical_mapper", &RunHierarchicalMapper);
2125 commands.emplace_back("image_deleter", &RunImageDeleter);
2126 commands.emplace_back("image_filterer", &RunImageFilterer);
2127 commands.emplace_back("image_rectifier", &RunImageRectifier);
2128 commands.emplace_back("image_registrator", &RunImageRegistrator);
2129 commands.emplace_back("image_undistorter", &RunImageUndistorter);
2130 commands.emplace_back("image_undistorter_standalone",
2131 &RunImageUndistorterStandalone);
2132 commands.emplace_back("mapper", &RunMapper);
2133 commands.emplace_back("matches_importer", &RunMatchesImporter);
2134 commands.emplace_back("model_aligner", &RunModelAligner);
2135 commands.emplace_back("model_analyzer", &RunModelAnalyzer);
2136 commands.emplace_back("model_converter", &RunModelConverter);
2137 commands.emplace_back("model_merger", &RunModelMerger);
2138 commands.emplace_back("model_orientation_aligner",
2139 &RunModelOrientationAligner);
2140 commands.emplace_back("patch_match_stereo", &RunPatchMatchStereo);
2141 commands.emplace_back("point_filtering", &RunPointFiltering);
2142 commands.emplace_back("point_triangulator", &RunPointTriangulator);
2143 commands.emplace_back("poisson_mesher", &RunPoissonMesher);
2144 commands.emplace_back("project_generator", &RunProjectGenerator);
2145 commands.emplace_back("rig_bundle_adjuster", &RunRigBundleAdjuster);
2146 commands.emplace_back("sequential_matcher", &RunSequentialMatcher);
2147 commands.emplace_back("spatial_matcher", &RunSpatialMatcher);
2148 commands.emplace_back("stereo_fusion", &RunStereoFuser);
2149 commands.emplace_back("transitive_matcher", &RunTransitiveMatcher);
2150 commands.emplace_back("vocab_tree_builder", &RunVocabTreeBuilder);
2151 commands.emplace_back("vocab_tree_matcher", &RunVocabTreeMatcher);
2152 commands.emplace_back("vocab_tree_retriever", &RunVocabTreeRetriever);
2153
2154 if (argc == 1) {
2155 return ShowHelp(commands);
2156 }
2157
2158 const std::string command = argv[1];
2159 if (command == "help" || command == "-h" || command == "--help") {
2160 return ShowHelp(commands);
2161 } else {
2162 command_func_t matched_command_func = nullptr;
2163 for (const auto& command_func : commands) {
2164 if (command == command_func.first) {
2165 matched_command_func = command_func.second;
2166 break;
2167 }
2168 }
2169 if (matched_command_func == nullptr) {
2170 std::cerr << StringPrintf(
2171 "ERROR: Command `%s` not recognized. To list the "
2172 "available commands, run `colmap help`.",
2173 command.c_str())
2174 << std::endl;
2175 return EXIT_FAILURE;
2176 } else {
2177 int command_argc = argc - 1;
2178 char** command_argv = &argv[1];
2179 command_argv[0] = argv[0];
2180 return matched_command_func(command_argc, command_argv);
2181 }
2182 }
2183
2184 return ShowHelp(commands);
2185 }
2186