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