ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
sfm.cc
Go to the documentation of this file.
1 // Copyright (c) 2018, ETH Zurich and UNC Chapel Hill.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 //
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 //
14 // * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of
15 // its contributors may be used to endorse or promote products derived
16 // from this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 // POSSIBILITY OF SUCH DAMAGE.
29 //
30 // Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de)
31 
32 #include "exe/sfm.h"
33 
34 #include <boost/property_tree/json_parser.hpp>
35 #include <boost/property_tree/ptree.hpp>
36 
37 #include "base/reconstruction.h"
41 #include "exe/gui.h"
42 #include "util/misc.h"
43 #include "util/opengl_utils.h"
44 #include "util/option_manager.h"
45 
46 namespace colmap {
47 
48 int RunAutomaticReconstructor(int argc, char** argv) {
49  AutomaticReconstructionController::Options reconstruction_options;
50  std::string data_type = "individual";
51  std::string quality = "high";
52  std::string mesher = "poisson";
53 
54  OptionManager options;
55  options.AddRequiredOption("workspace_path",
56  &reconstruction_options.workspace_path);
57  options.AddRequiredOption("image_path", &reconstruction_options.image_path);
58  options.AddDefaultOption("mask_path", &reconstruction_options.mask_path);
59  options.AddDefaultOption("vocab_tree_path",
60  &reconstruction_options.vocab_tree_path);
61  options.AddDefaultOption("data_type", &data_type,
62  "{individual, video, internet}");
63  options.AddDefaultOption("quality", &quality, "{low, medium, high, extreme}");
64  options.AddDefaultOption("camera_model",
65  &reconstruction_options.camera_model);
66  options.AddDefaultOption("single_camera",
67  &reconstruction_options.single_camera);
68  options.AddDefaultOption("sparse", &reconstruction_options.sparse);
69  options.AddDefaultOption("dense", &reconstruction_options.dense);
70  options.AddDefaultOption("mesher", &mesher, "{poisson, delaunay}");
71  options.AddDefaultOption("num_threads", &reconstruction_options.num_threads);
72  options.AddDefaultOption("use_gpu", &reconstruction_options.use_gpu);
73  options.AddDefaultOption("gpu_index", &reconstruction_options.gpu_index);
74  options.Parse(argc, argv);
75 
76  StringToLower(&data_type);
77  if (data_type == "individual") {
78  reconstruction_options.data_type =
80  } else if (data_type == "video") {
81  reconstruction_options.data_type =
83  } else if (data_type == "internet") {
84  reconstruction_options.data_type =
86  } else {
87  LOG(FATAL) << "Invalid data type provided";
88  }
89 
90  StringToLower(&quality);
91  if (quality == "low") {
92  reconstruction_options.quality =
94  } else if (quality == "medium") {
95  reconstruction_options.quality =
97  } else if (quality == "high") {
98  reconstruction_options.quality =
100  } else if (quality == "extreme") {
101  reconstruction_options.quality =
103  } else {
104  LOG(FATAL) << "Invalid quality provided";
105  }
106 
107  StringToLower(&mesher);
108  if (mesher == "poisson") {
109  reconstruction_options.mesher =
111  } else if (mesher == "delaunay") {
112  reconstruction_options.mesher =
114  } else {
115  LOG(FATAL) << "Invalid mesher provided";
116  }
117 
118  ReconstructionManager reconstruction_manager;
119 
120  if (reconstruction_options.use_gpu && kUseOpenGL) {
121  QApplication app(argc, argv);
122  AutomaticReconstructionController controller(reconstruction_options,
123  &reconstruction_manager);
124  RunThreadWithOpenGLContext(&controller);
125  } else {
126  AutomaticReconstructionController controller(reconstruction_options,
127  &reconstruction_manager);
128  controller.Start();
129  controller.Wait();
130  }
131 
132  return EXIT_SUCCESS;
133 }
134 
135 int RunBundleAdjuster(int argc, char** argv) {
136  std::string input_path;
137  std::string output_path;
138 
139  OptionManager options;
140  options.AddRequiredOption("input_path", &input_path);
141  options.AddRequiredOption("output_path", &output_path);
142  options.AddBundleAdjustmentOptions();
143  options.Parse(argc, argv);
144 
145  if (!ExistsDir(input_path)) {
146  std::cerr << "ERROR: `input_path` is not a directory" << std::endl;
147  return EXIT_FAILURE;
148  }
149 
150  if (!ExistsDir(output_path)) {
151  std::cerr << "ERROR: `output_path` is not a directory" << std::endl;
152  return EXIT_FAILURE;
153  }
154 
155  Reconstruction reconstruction;
156  reconstruction.Read(input_path);
157 
158  BundleAdjustmentController ba_controller(options, &reconstruction);
159  ba_controller.Start();
160  ba_controller.Wait();
161 
162  reconstruction.Write(output_path);
163 
164  return EXIT_SUCCESS;
165 }
166 
167 int RunColorExtractor(int argc, char** argv) {
168  std::string input_path;
169  std::string output_path;
170 
171  OptionManager options;
172  options.AddImageOptions();
173  options.AddDefaultOption("input_path", &input_path);
174  options.AddRequiredOption("output_path", &output_path);
175  options.Parse(argc, argv);
176 
177  Reconstruction reconstruction;
178  reconstruction.Read(input_path);
179  reconstruction.ExtractColorsForAllImages(*options.image_path);
180  reconstruction.Write(output_path);
181 
182  return EXIT_SUCCESS;
183 }
184 
185 int RunMapper(int argc, char** argv) {
186  std::string input_path;
187  std::string output_path;
188  std::string image_list_path;
189 
190  OptionManager options;
191  options.AddDatabaseOptions();
192  options.AddImageOptions();
193  options.AddDefaultOption("input_path", &input_path);
194  options.AddRequiredOption("output_path", &output_path);
195  options.AddDefaultOption("image_list_path", &image_list_path);
196  options.AddMapperOptions();
197  options.Parse(argc, argv);
198 
199  if (!ExistsDir(output_path)) {
200  std::cerr << "ERROR: `output_path` is not a directory." << std::endl;
201  return EXIT_FAILURE;
202  }
203 
204  if (!image_list_path.empty()) {
205  const auto image_names = ReadTextFileLines(image_list_path);
206  options.mapper->image_names =
207  std::unordered_set<std::string>(image_names.begin(), image_names.end());
208  }
209 
210  ReconstructionManager reconstruction_manager;
211  if (input_path != "") {
212  if (!ExistsDir(input_path)) {
213  std::cerr << "ERROR: `input_path` is not a directory." << std::endl;
214  return EXIT_FAILURE;
215  }
216  reconstruction_manager.Read(input_path);
217  }
218 
219  IncrementalMapperController mapper(options.mapper.get(), *options.image_path,
220  *options.database_path,
221  &reconstruction_manager);
222 
223  // In case a new reconstruction is started, write results of individual sub-
224  // models to as their reconstruction finishes instead of writing all results
225  // after all reconstructions finished.
226  size_t prev_num_reconstructions = 0;
227  if (input_path == "") {
228  mapper.AddCallback(
230  // If the number of reconstructions has not changed, the last model
231  // was discarded for some reason.
232  if (reconstruction_manager.Size() > prev_num_reconstructions) {
233  const std::string reconstruction_path = JoinPaths(
234  output_path, std::to_string(prev_num_reconstructions));
235  const auto& reconstruction =
236  reconstruction_manager.Get(prev_num_reconstructions);
237  CreateDirIfNotExists(reconstruction_path);
238  reconstruction.Write(reconstruction_path);
239  options.Write(JoinPaths(reconstruction_path, "project.ini"));
240  prev_num_reconstructions = reconstruction_manager.Size();
241  }
242  });
243  }
244 
245  mapper.Start();
246  mapper.Wait();
247 
248  if (reconstruction_manager.Size() == 0) {
249  std::cerr << "ERROR: failed to create sparse model" << std::endl;
250  return EXIT_FAILURE;
251  }
252 
253  // In case the reconstruction is continued from an existing reconstruction, do
254  // not create sub-folders but directly write the results.
255  if (input_path != "" && reconstruction_manager.Size() > 0) {
256  reconstruction_manager.Get(0).Write(output_path);
257  }
258 
259  return EXIT_SUCCESS;
260 }
261 
262 int RunHierarchicalMapper(int argc, char** argv) {
263  HierarchicalMapperController::Options hierarchical_options;
264  SceneClustering::Options clustering_options;
265  std::string output_path;
266 
267  OptionManager options;
268  options.AddRequiredOption("database_path",
269  &hierarchical_options.database_path);
270  options.AddRequiredOption("image_path", &hierarchical_options.image_path);
271  options.AddRequiredOption("output_path", &output_path);
272  options.AddDefaultOption("num_workers", &hierarchical_options.num_workers);
273  options.AddDefaultOption("image_overlap", &clustering_options.image_overlap);
274  options.AddDefaultOption("leaf_max_num_images",
275  &clustering_options.leaf_max_num_images);
276  options.AddMapperOptions();
277  options.Parse(argc, argv);
278 
279  if (!ExistsDir(output_path)) {
280  std::cerr << "ERROR: `output_path` is not a directory." << std::endl;
281  return EXIT_FAILURE;
282  }
283 
284  ReconstructionManager reconstruction_manager;
285 
286  HierarchicalMapperController hierarchical_mapper(
287  hierarchical_options, clustering_options, *options.mapper,
288  &reconstruction_manager);
289  hierarchical_mapper.Start();
290  hierarchical_mapper.Wait();
291 
292  if (reconstruction_manager.Size() == 0) {
293  std::cerr << "ERROR: failed to create sparse model" << std::endl;
294  return EXIT_FAILURE;
295  }
296 
297  reconstruction_manager.Write(output_path, &options);
298 
299  return EXIT_SUCCESS;
300 }
301 
302 int RunPointFiltering(int argc, char** argv) {
303  std::string input_path;
304  std::string output_path;
305 
306  size_t min_track_len = 2;
307  double max_reproj_error = 4.0;
308  double min_tri_angle = 1.5;
309 
310  OptionManager options;
311  options.AddRequiredOption("input_path", &input_path);
312  options.AddRequiredOption("output_path", &output_path);
313  options.AddDefaultOption("min_track_len", &min_track_len);
314  options.AddDefaultOption("max_reproj_error", &max_reproj_error);
315  options.AddDefaultOption("min_tri_angle", &min_tri_angle);
316  options.Parse(argc, argv);
317 
318  Reconstruction reconstruction;
319  reconstruction.Read(input_path);
320 
321  size_t num_filtered =
322  reconstruction.FilterAllPoints3D(max_reproj_error, min_tri_angle);
323 
324  for (const auto point3D_id : reconstruction.Point3DIds()) {
325  const auto& point3D = reconstruction.Point3D(point3D_id);
326  if (point3D.Track().Length() < min_track_len) {
327  num_filtered += point3D.Track().Length();
328  reconstruction.DeletePoint3D(point3D_id);
329  }
330  }
331 
332  std::cout << "Filtered observations: " << num_filtered << std::endl;
333 
334  reconstruction.Write(output_path);
335 
336  return EXIT_SUCCESS;
337 }
338 
339 int RunPointTriangulator(int argc, char** argv) {
340  std::string input_path;
341  std::string output_path;
342  bool clear_points = false;
343 
344  OptionManager options;
345  options.AddDatabaseOptions();
346  options.AddImageOptions();
347  options.AddRequiredOption("input_path", &input_path);
348  options.AddRequiredOption("output_path", &output_path);
349  options.AddDefaultOption(
350  "clear_points", &clear_points,
351  "Whether to clear all existing points and observations");
352  options.AddMapperOptions();
353  options.Parse(argc, argv);
354 
355  if (!ExistsDir(input_path)) {
356  std::cerr << "ERROR: `input_path` is not a directory" << std::endl;
357  return EXIT_FAILURE;
358  }
359 
360  if (!ExistsDir(output_path)) {
361  std::cerr << "ERROR: `output_path` is not a directory" << std::endl;
362  return EXIT_FAILURE;
363  }
364 
365  const auto& mapper_options = *options.mapper;
366 
367  PrintHeading1("Loading model");
368 
369  Reconstruction reconstruction;
370  reconstruction.Read(input_path);
371 
372  PrintHeading1("Loading database");
373 
374  DatabaseCache database_cache;
375 
376  {
377  Timer timer;
378  timer.Start();
379 
380  Database database(*options.database_path);
381 
382  const size_t min_num_matches =
383  static_cast<size_t>(mapper_options.min_num_matches);
384  database_cache.Load(database, min_num_matches,
385  mapper_options.ignore_watermarks,
386  mapper_options.image_names);
387 
388  if (clear_points) {
389  reconstruction.DeleteAllPoints2DAndPoints3D();
390  reconstruction.TranscribeImageIdsToDatabase(database);
391  }
392 
393  std::cout << std::endl;
394  timer.PrintMinutes();
395  }
396 
397  std::cout << std::endl;
398 
399  CHECK_GE(reconstruction.NumRegImages(), 2)
400  << "Need at least two images for triangulation";
401 
402  IncrementalMapper mapper(&database_cache);
403  mapper.BeginReconstruction(&reconstruction);
404 
406  // Triangulation
408 
409  const auto tri_options = mapper_options.Triangulation();
410 
411  const auto& reg_image_ids = reconstruction.RegImageIds();
412 
413  for (size_t i = 0; i < reg_image_ids.size(); ++i) {
414  const image_t image_id = reg_image_ids[i];
415 
416  const auto& image = reconstruction.Image(image_id);
417 
418  PrintHeading1(StringPrintf("Triangulating image #%d (%d)", image_id, i));
419 
420  const size_t num_existing_points3D = image.NumPoints3D();
421 
422  std::cout << " => Image sees " << num_existing_points3D << " / "
423  << image.NumObservations() << " points" << std::endl;
424 
425  mapper.TriangulateImage(tri_options, image_id);
426 
427  std::cout << " => Triangulated "
428  << (image.NumPoints3D() - num_existing_points3D) << " points"
429  << std::endl;
430  }
431 
433  // Retriangulation
435 
436  PrintHeading1("Retriangulation");
437 
438  CompleteAndMergeTracks(mapper_options, &mapper);
439 
441  // Bundle adjustment
443 
444  auto ba_options = mapper_options.GlobalBundleAdjustment();
445  ba_options.refine_focal_length = false;
446  ba_options.refine_principal_point = false;
447  ba_options.refine_extra_params = false;
448  ba_options.refine_extrinsics = false;
449 
450  // Configure bundle adjustment.
451  BundleAdjustmentConfig ba_config;
452  for (const image_t image_id : reconstruction.RegImageIds()) {
453  ba_config.AddImage(image_id);
454  }
455 
456  for (int i = 0; i < mapper_options.ba_global_max_refinements; ++i) {
457  // Avoid degeneracies in bundle adjustment.
458  reconstruction.FilterObservationsWithNegativeDepth();
459 
460  const size_t num_observations = reconstruction.ComputeNumObservations();
461 
462  PrintHeading1("Bundle adjustment");
463  BundleAdjuster bundle_adjuster(ba_options, ba_config);
464  CHECK(bundle_adjuster.Solve(&reconstruction));
465 
466  size_t num_changed_observations = 0;
467  num_changed_observations += CompleteAndMergeTracks(mapper_options, &mapper);
468  num_changed_observations += FilterPoints(mapper_options, &mapper);
469  const double changed =
470  static_cast<double>(num_changed_observations) / num_observations;
471  std::cout << StringPrintf(" => Changed observations: %.6f", changed)
472  << std::endl;
473  if (changed < mapper_options.ba_global_max_refinement_change) {
474  break;
475  }
476  }
477 
478  PrintHeading1("Extracting colors");
479  reconstruction.ExtractColorsForAllImages(*options.image_path);
480 
481  const bool kDiscardReconstruction = false;
482  mapper.EndReconstruction(kDiscardReconstruction);
483 
484  reconstruction.Write(output_path);
485 
486  return EXIT_SUCCESS;
487 }
488 
489 namespace {
490 
491 // Read the configuration of the camera rigs from a JSON file. The input images
492 // of a camera rig must be named consistently to assign them to the appropriate
493 // camera rig and the respective snapshots.
494 //
495 // An example configuration of a single camera rig:
496 // [
497 // {
498 // "ref_camera_id": 1,
499 // "cameras":
500 // [
501 // {
502 // "camera_id": 1,
503 // "image_prefix": "left1_image"
504 // "rel_tvec": [0, 0, 0],
505 // "rel_qvec": [1, 0, 0, 0]
506 // },
507 // {
508 // "camera_id": 2,
509 // "image_prefix": "left2_image"
510 // "rel_tvec": [0, 0, 0],
511 // "rel_qvec": [0, 1, 0, 0]
512 // },
513 // {
514 // "camera_id": 3,
515 // "image_prefix": "right1_image"
516 // "rel_tvec": [0, 0, 0],
517 // "rel_qvec": [0, 0, 1, 0]
518 // },
519 // {
520 // "camera_id": 4,
521 // "image_prefix": "right2_image"
522 // "rel_tvec": [0, 0, 0],
523 // "rel_qvec": [0, 0, 0, 1]
524 // }
525 // ]
526 // }
527 // ]
528 //
529 // The "camera_id" and "image_prefix" fields are required, whereas the
530 // "rel_tvec" and "rel_qvec" fields optionally specify the relative
531 // extrinsics of the camera rig in the form of a translation vector and a
532 // rotation quaternion. The relative extrinsics rel_qvec and rel_tvec transform
533 // coordinates from rig to camera coordinate space. If the relative extrinsics
534 // are not provided then they are automatically inferred from the
535 // reconstruction.
536 //
537 // This file specifies the configuration for a single camera rig and that you
538 // could potentially define multiple camera rigs. The rig is composed of 4
539 // cameras: all images of the first camera must have "left1_image" as a name
540 // prefix, e.g., "left1_image_frame000.png" or "left1_image/frame000.png".
541 // Images with the same suffix ("_frame000.png" and "/frame000.png") are
542 // assigned to the same snapshot, i.e., they are assumed to be captured at the
543 // same time. Only snapshots with the reference image registered will be added
544 // to the bundle adjustment problem. The remaining images will be added with
545 // independent poses to the bundle adjustment problem. The above configuration
546 // could have the following input image file structure:
547 //
548 // /path/to/images/...
549 // left1_image/...
550 // frame000.png
551 // frame001.png
552 // frame002.png
553 // ...
554 // left2_image/...
555 // frame000.png
556 // frame001.png
557 // frame002.png
558 // ...
559 // right1_image/...
560 // frame000.png
561 // frame001.png
562 // frame002.png
563 // ...
564 // right2_image/...
565 // frame000.png
566 // frame001.png
567 // frame002.png
568 // ...
569 //
570 std::vector<CameraRig> ReadCameraRigConfig(const std::string& rig_config_path,
571  const Reconstruction& reconstruction,
572  bool estimate_rig_relative_poses) {
573  boost::property_tree::ptree pt;
574  boost::property_tree::read_json(rig_config_path.c_str(), pt);
575 
576  std::vector<CameraRig> camera_rigs;
577  for (const auto& rig_config : pt) {
578  CameraRig camera_rig;
579 
580  std::vector<std::string> image_prefixes;
581  for (const auto& camera : rig_config.second.get_child("cameras")) {
582  const int camera_id = camera.second.get<int>("camera_id");
583  image_prefixes.push_back(camera.second.get<std::string>("image_prefix"));
584  Eigen::Vector3d rel_tvec;
585  Eigen::Vector4d rel_qvec;
586  int index = 0;
587  auto rel_tvec_node = camera.second.get_child_optional("rel_tvec");
588  if (rel_tvec_node) {
589  for (const auto& node : rel_tvec_node.get()) {
590  rel_tvec[index++] = node.second.get_value<double>();
591  }
592  } else {
593  estimate_rig_relative_poses = true;
594  }
595  index = 0;
596  auto rel_qvec_node = camera.second.get_child_optional("rel_qvec");
597  if (rel_qvec_node) {
598  for (const auto& node : rel_qvec_node.get()) {
599  rel_qvec[index++] = node.second.get_value<double>();
600  }
601  } else {
602  estimate_rig_relative_poses = true;
603  }
604 
605  camera_rig.AddCamera(camera_id, rel_qvec, rel_tvec);
606  }
607 
608  camera_rig.SetRefCameraId(rig_config.second.get<int>("ref_camera_id"));
609 
610  std::unordered_map<std::string, std::vector<image_t>> snapshots;
611  for (const auto image_id : reconstruction.RegImageIds()) {
612  const auto& image = reconstruction.Image(image_id);
613  for (const auto& image_prefix : image_prefixes) {
614  if (StringContains(image.Name(), image_prefix)) {
615  const std::string image_suffix =
616  StringGetAfter(image.Name(), image_prefix);
617  snapshots[image_suffix].push_back(image_id);
618  }
619  }
620  }
621 
622  for (const auto& snapshot : snapshots) {
623  bool has_ref_camera = false;
624  for (const auto image_id : snapshot.second) {
625  const auto& image = reconstruction.Image(image_id);
626  if (image.CameraId() == camera_rig.RefCameraId()) {
627  has_ref_camera = true;
628  }
629  }
630 
631  if (has_ref_camera) {
632  camera_rig.AddSnapshot(snapshot.second);
633  }
634  }
635 
636  camera_rig.Check(reconstruction);
637  if (estimate_rig_relative_poses) {
638  PrintHeading2("Estimating relative rig poses");
639  if (!camera_rig.ComputeRelativePoses(reconstruction)) {
640  std::cout << "WARN: Failed to estimate rig poses from reconstruction; "
641  "cannot use rig BA"
642  << std::endl;
643  return std::vector<CameraRig>();
644  }
645  }
646 
647  camera_rigs.push_back(camera_rig);
648  }
649 
650  return camera_rigs;
651 }
652 
653 } // namespace
654 
655 int RunRigBundleAdjuster(int argc, char** argv) {
656  std::string input_path;
657  std::string output_path;
658  std::string rig_config_path;
659  bool estimate_rig_relative_poses = true;
660 
661  RigBundleAdjuster::Options rig_ba_options;
662 
663  OptionManager options;
664  options.AddRequiredOption("input_path", &input_path);
665  options.AddRequiredOption("output_path", &output_path);
666  options.AddRequiredOption("rig_config_path", &rig_config_path);
667  options.AddDefaultOption("estimate_rig_relative_poses",
668  &estimate_rig_relative_poses);
669  options.AddDefaultOption("RigBundleAdjustment.refine_relative_poses",
670  &rig_ba_options.refine_relative_poses);
671  options.AddBundleAdjustmentOptions();
672  options.Parse(argc, argv);
673 
674  Reconstruction reconstruction;
675  reconstruction.Read(input_path);
676 
677  PrintHeading1("Camera rig configuration");
678 
679  auto camera_rigs = ReadCameraRigConfig(rig_config_path, reconstruction,
680  estimate_rig_relative_poses);
681 
682  BundleAdjustmentConfig config;
683  for (size_t i = 0; i < camera_rigs.size(); ++i) {
684  const auto& camera_rig = camera_rigs[i];
685  PrintHeading2(StringPrintf("Camera Rig %d", i + 1));
686  std::cout << StringPrintf("Cameras: %d", camera_rig.NumCameras())
687  << std::endl;
688  std::cout << StringPrintf("Snapshots: %d", camera_rig.NumSnapshots())
689  << std::endl;
690 
691  // Add all registered images to the bundle adjustment configuration.
692  for (const auto image_id : reconstruction.RegImageIds()) {
693  config.AddImage(image_id);
694  }
695  }
696 
697  PrintHeading1("Rig bundle adjustment");
698 
699  BundleAdjustmentOptions ba_options = *options.bundle_adjustment;
700  ba_options.solver_options.minimizer_progress_to_stdout = true;
701  RigBundleAdjuster bundle_adjuster(ba_options, rig_ba_options, config);
702  CHECK(bundle_adjuster.Solve(&reconstruction, &camera_rigs));
703 
704  reconstruction.Write(output_path);
705 
706  return EXIT_SUCCESS;
707 }
708 
709 } // namespace colmap
std::shared_ptr< core::Tensor > image
bool Solve(Reconstruction *reconstruction)
void AddImage(const image_t image_id)
void Load(const Database &database, const size_t min_num_matches, const bool ignore_watermarks, const std::unordered_set< std::string > &image_names)
void EndReconstruction(const bool discard)
void BeginReconstruction(Reconstruction *reconstruction)
size_t TriangulateImage(const IncrementalTriangulator::Options &tri_options, const image_t image_id)
void AddRequiredOption(const std::string &name, T *option, const std::string &help_text="")
void AddDefaultOption(const std::string &name, T *option, const std::string &help_text="")
std::shared_ptr< std::string > database_path
std::shared_ptr< BundleAdjustmentOptions > bundle_adjustment
std::shared_ptr< IncrementalMapperOptions > mapper
void Parse(const int argc, char **argv)
std::shared_ptr< std::string > image_path
const class Track & Track() const
Definition: point3d.h:106
const Reconstruction & Get(const size_t idx) const
size_t Read(const std::string &path)
void Write(const std::string &path, const OptionManager *options) const
void ExtractColorsForAllImages(const std::string &path)
size_t FilterAllPoints3D(const double max_reproj_error, const double min_tri_angle)
size_t NumRegImages() const
size_t FilterObservationsWithNegativeDepth()
void Write(const std::string &path) const
size_t ComputeNumObservations() const
const class Image & Image(const image_t image_id) const
std::unordered_set< point3D_t > Point3DIds() const
const std::vector< image_t > & RegImageIds() const
void DeletePoint3D(const point3D_t point3D_id)
void Read(const std::string &path)
const class Point3D & Point3D(const point3D_t point3D_id) const
void TranscribeImageIdsToDatabase(const Database &database)
bool Solve(Reconstruction *reconstruction, std::vector< CameraRig > *camera_rigs)
virtual void Start()
Definition: threading.cc:50
void AddCallback(const int id, const std::function< void()> &func)
Definition: threading.cc:117
virtual void Wait()
Definition: threading.cc:86
void Start()
Definition: timer.cc:43
void PrintMinutes() const
Definition: timer.cc:93
size_t Length() const
Definition: track.h:82
QTextStream & endl(QTextStream &stream)
Definition: QtCompat.h:718
std::string StringGetAfter(const std::string &str, const std::string &key)
Definition: string.cc:154
void PrintHeading2(const std::string &heading)
Definition: misc.cc:231
const bool kUseOpenGL
Definition: gui.h:25
void RunThreadWithOpenGLContext(Thread *thread)
Definition: opengl_utils.h:81
int RunColorExtractor(int argc, char **argv)
Definition: sfm.cc:167
void StringToLower(std::string *str)
Definition: string.cc:193
size_t CompleteAndMergeTracks(const IncrementalMapperOptions &options, IncrementalMapper *mapper)
int RunPointTriangulator(int argc, char **argv)
Definition: sfm.cc:339
int RunHierarchicalMapper(int argc, char **argv)
Definition: sfm.cc:262
std::vector< std::string > ReadTextFileLines(const std::string &path)
Definition: misc.cc:308
bool ExistsDir(const std::string &path)
Definition: misc.cc:104
int RunMapper(int argc, char **argv)
Definition: sfm.cc:185
int RunRigBundleAdjuster(int argc, char **argv)
Definition: sfm.cc:655
uint32_t image_t
Definition: types.h:61
void PrintHeading1(const std::string &heading)
Definition: misc.cc:225
std::string StringPrintf(const char *format,...)
Definition: string.cc:131
int RunAutomaticReconstructor(int argc, char **argv)
Definition: sfm.cc:48
int RunBundleAdjuster(int argc, char **argv)
Definition: sfm.cc:135
bool StringContains(const std::string &str, const std::string &sub_str)
Definition: string.cc:201
int RunPointFiltering(int argc, char **argv)
Definition: sfm.cc:302
size_t FilterPoints(const IncrementalMapperOptions &options, IncrementalMapper *mapper)
ceres::Solver::Options solver_options