ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
incremental_mapper.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 "sfm/incremental_mapper.h"
33 
34 #include <array>
35 #include <fstream>
36 
37 #include "base/projection.h"
38 #include "base/triangulation.h"
39 #include "estimators/pose.h"
40 #include "util/bitmap.h"
41 #include "util/misc.h"
42 
43 namespace colmap {
44 namespace {
45 
46 void SortAndAppendNextImages(std::vector<std::pair<image_t, float>> image_ranks,
47  std::vector<image_t>* sorted_images_ids) {
48  std::sort(image_ranks.begin(), image_ranks.end(),
49  [](const std::pair<image_t, float>& image1,
50  const std::pair<image_t, float>& image2) {
51  return image1.second > image2.second;
52  });
53 
54  sorted_images_ids->reserve(sorted_images_ids->size() + image_ranks.size());
55  for (const auto& image : image_ranks) {
56  sorted_images_ids->push_back(image.first);
57  }
58 
59  image_ranks.clear();
60 }
61 
62 float RankNextImageMaxVisiblePointsNum(const Image& image) {
63  return static_cast<float>(image.NumVisiblePoints3D());
64 }
65 
66 float RankNextImageMaxVisiblePointsRatio(const Image& image) {
67  return static_cast<float>(image.NumVisiblePoints3D()) /
68  static_cast<float>(image.NumObservations());
69 }
70 
71 float RankNextImageMinUncertainty(const Image& image) {
72  return static_cast<float>(image.Point3DVisibilityScore());
73 }
74 
75 } // namespace
76 
96  return true;
97 }
98 
100  : database_cache_(database_cache),
101  reconstruction_(nullptr),
102  triangulator_(nullptr),
103  num_total_reg_images_(0),
104  num_shared_reg_images_(0),
105  prev_init_image_pair_id_(kInvalidImagePairId) {}
106 
108  CHECK(reconstruction_ == nullptr);
109  reconstruction_ = reconstruction;
110  reconstruction_->Load(*database_cache_);
111  reconstruction_->SetUp(&database_cache_->CorrespondenceGraph());
112  triangulator_.reset(new IncrementalTriangulator(
113  &database_cache_->CorrespondenceGraph(), reconstruction));
114 
115  num_shared_reg_images_ = 0;
116  num_reg_images_per_camera_.clear();
117  for (const image_t image_id : reconstruction_->RegImageIds()) {
118  RegisterImageEvent(image_id);
119  }
120 
121  existing_image_ids_ =
122  std::unordered_set<image_t>(reconstruction->RegImageIds().begin(),
123  reconstruction->RegImageIds().end());
124 
125  prev_init_image_pair_id_ = kInvalidImagePairId;
126  prev_init_two_view_geometry_ = TwoViewGeometry();
127 
128  filtered_images_.clear();
129  num_reg_trials_.clear();
130 }
131 
132 void IncrementalMapper::EndReconstruction(const bool discard) {
133  CHECK_NOTNULL(reconstruction_);
134 
135  if (discard) {
136  for (const image_t image_id : reconstruction_->RegImageIds()) {
137  DeRegisterImageEvent(image_id);
138  }
139  }
140 
141  reconstruction_->TearDown();
142  reconstruction_ = nullptr;
143  triangulator_.reset();
144 }
145 
147  image_t* image_id1,
148  image_t* image_id2) {
149  CHECK(options.Check());
150 
151  std::vector<image_t> image_ids1;
152  if (*image_id1 != kInvalidImageId && *image_id2 == kInvalidImageId) {
153  // Only *image_id1 provided.
154  if (!database_cache_->ExistsImage(*image_id1)) {
155  return false;
156  }
157  image_ids1.push_back(*image_id1);
158  } else if (*image_id1 == kInvalidImageId && *image_id2 != kInvalidImageId) {
159  // Only *image_id2 provided.
160  if (!database_cache_->ExistsImage(*image_id2)) {
161  return false;
162  }
163  image_ids1.push_back(*image_id2);
164  } else {
165  // No initial seed image provided.
166  image_ids1 = FindFirstInitialImage(options);
167  }
168 
169  // Try to find good initial pair.
170  for (size_t i1 = 0; i1 < image_ids1.size(); ++i1) {
171  *image_id1 = image_ids1[i1];
172 
173  const std::vector<image_t> image_ids2 =
174  FindSecondInitialImage(options, *image_id1);
175 
176  for (size_t i2 = 0; i2 < image_ids2.size(); ++i2) {
177  *image_id2 = image_ids2[i2];
178 
179  const image_pair_t pair_id =
180  Database::ImagePairToPairId(*image_id1, *image_id2);
181 
182  // Try every pair only once.
183  if (init_image_pairs_.count(pair_id) > 0) {
184  continue;
185  }
186 
187  init_image_pairs_.insert(pair_id);
188 
189  if (EstimateInitialTwoViewGeometry(options, *image_id1,
190  *image_id2)) {
191  return true;
192  }
193  }
194  }
195 
196  // No suitable pair found in entire dataset.
197  *image_id1 = kInvalidImageId;
198  *image_id2 = kInvalidImageId;
199 
200  return false;
201 }
202 
203 std::vector<image_t> IncrementalMapper::FindNextImages(const Options& options) {
204  CHECK_NOTNULL(reconstruction_);
205  CHECK(options.Check());
206 
207  std::function<float(const Image&)> rank_image_func;
208  switch (options.image_selection_method) {
210  rank_image_func = RankNextImageMaxVisiblePointsNum;
211  break;
213  rank_image_func = RankNextImageMaxVisiblePointsRatio;
214  break;
216  rank_image_func = RankNextImageMinUncertainty;
217  break;
218  }
219 
220  std::vector<std::pair<image_t, float>> image_ranks;
221  std::vector<std::pair<image_t, float>> other_image_ranks;
222 
223  // Append images that have not failed to register before.
224  for (const auto& image : reconstruction_->Images()) {
225  // Skip images that are already registered.
226  if (image.second.IsRegistered()) {
227  continue;
228  }
229 
230  // Only consider images with a sufficient number of visible points.
231  if (image.second.NumVisiblePoints3D() <
232  static_cast<size_t>(options.abs_pose_min_num_inliers)) {
233  continue;
234  }
235 
236  // Only try registration for a certain maximum number of times.
237  const size_t num_reg_trials = num_reg_trials_[image.first];
238  if (num_reg_trials >= static_cast<size_t>(options.max_reg_trials)) {
239  continue;
240  }
241 
242  // If image has been filtered or failed to register, place it in the
243  // second bucket and prefer images that have not been tried before.
244  const float rank = rank_image_func(image.second);
245  if (filtered_images_.count(image.first) == 0 && num_reg_trials == 0) {
246  image_ranks.emplace_back(image.first, rank);
247  } else {
248  other_image_ranks.emplace_back(image.first, rank);
249  }
250  }
251 
252  std::vector<image_t> ranked_images_ids;
253  SortAndAppendNextImages(image_ranks, &ranked_images_ids);
254  SortAndAppendNextImages(other_image_ranks, &ranked_images_ids);
255 
256  return ranked_images_ids;
257 }
258 
260  const image_t image_id1,
261  const image_t image_id2) {
262  CHECK_NOTNULL(reconstruction_);
263  CHECK_EQ(reconstruction_->NumRegImages(), 0);
264 
265  CHECK(options.Check());
266 
267  init_num_reg_trials_[image_id1] += 1;
268  init_num_reg_trials_[image_id2] += 1;
269  num_reg_trials_[image_id1] += 1;
270  num_reg_trials_[image_id2] += 1;
271 
272  const image_pair_t pair_id =
273  Database::ImagePairToPairId(image_id1, image_id2);
274  init_image_pairs_.insert(pair_id);
275 
276  Image& image1 = reconstruction_->Image(image_id1);
277  const Camera& camera1 = reconstruction_->Camera(image1.CameraId());
278 
279  Image& image2 = reconstruction_->Image(image_id2);
280  const Camera& camera2 = reconstruction_->Camera(image2.CameraId());
281 
283  // Estimate two-view geometry
285 
286  if (!EstimateInitialTwoViewGeometry(options, image_id1, image_id2)) {
287  return false;
288  }
289 
290  image1.Qvec() = ComposeIdentityQuaternion();
291  image1.Tvec() = Eigen::Vector3d(0, 0, 0);
292  image2.Qvec() = prev_init_two_view_geometry_.qvec;
293  image2.Tvec() = prev_init_two_view_geometry_.tvec;
294 
295  const Eigen::Matrix3x4d proj_matrix1 = image1.ProjectionMatrix();
296  const Eigen::Matrix3x4d proj_matrix2 = image2.ProjectionMatrix();
297  const Eigen::Vector3d proj_center1 = image1.ProjectionCenter();
298  const Eigen::Vector3d proj_center2 = image2.ProjectionCenter();
299 
301  // Update Reconstruction
303 
304  reconstruction_->RegisterImage(image_id1);
305  reconstruction_->RegisterImage(image_id2);
306  RegisterImageEvent(image_id1);
307  RegisterImageEvent(image_id2);
308 
309  const CorrespondenceGraph& correspondence_graph =
310  database_cache_->CorrespondenceGraph();
311  const FeatureMatches& corrs =
312  correspondence_graph.FindCorrespondencesBetweenImages(image_id1,
313  image_id2);
314 
315  const double min_tri_angle_rad = DegToRad(options.init_min_tri_angle);
316 
317  // Add 3D point tracks.
318  Track track;
319  track.Reserve(2);
320  track.AddElement(TrackElement());
321  track.AddElement(TrackElement());
322  track.Element(0).image_id = image_id1;
323  track.Element(1).image_id = image_id2;
324  for (const auto& corr : corrs) {
325  const Eigen::Vector2d point1_N =
326  camera1.ImageToWorld(image1.Point2D(corr.point2D_idx1).XY());
327  const Eigen::Vector2d point2_N =
328  camera2.ImageToWorld(image2.Point2D(corr.point2D_idx2).XY());
329  const Eigen::Vector3d& xyz = TriangulatePoint(
330  proj_matrix1, proj_matrix2, point1_N, point2_N);
331  const double tri_angle =
332  CalculateTriangulationAngle(proj_center1, proj_center2, xyz);
333  if (tri_angle >= min_tri_angle_rad &&
334  HasPointPositiveDepth(proj_matrix1, xyz) &&
335  HasPointPositiveDepth(proj_matrix2, xyz)) {
336  track.Element(0).point2D_idx = corr.point2D_idx1;
337  track.Element(1).point2D_idx = corr.point2D_idx2;
338  reconstruction_->AddPoint3D(xyz, track);
339  }
340  }
341 
342  return true;
343 }
344 
346  const image_t image_id) {
347  CHECK_NOTNULL(reconstruction_);
348  CHECK_GE(reconstruction_->NumRegImages(), 2);
349 
350  CHECK(options.Check());
351 
352  Image& image = reconstruction_->Image(image_id);
353  Camera& camera = reconstruction_->Camera(image.CameraId());
354 
355  CHECK(!image.IsRegistered()) << "Image cannot be registered multiple times";
356 
357  num_reg_trials_[image_id] += 1;
358 
359  // Check if enough 2D-3D correspondences.
360  if (image.NumVisiblePoints3D() <
361  static_cast<size_t>(options.abs_pose_min_num_inliers)) {
362  return false;
363  }
364 
366  // Search for 2D-3D correspondences
368 
369  const int kCorrTransitivity = 1;
370 
371  std::vector<std::pair<point2D_t, point3D_t>> tri_corrs;
372  std::vector<Eigen::Vector2d> tri_points2D;
373  std::vector<Eigen::Vector3d> tri_points3D;
374 
375  for (point2D_t point2D_idx = 0; point2D_idx < image.NumPoints2D();
376  ++point2D_idx) {
377  const Point2D& point2D = image.Point2D(point2D_idx);
378  const CorrespondenceGraph& correspondence_graph =
379  database_cache_->CorrespondenceGraph();
380  const std::vector<CorrespondenceGraph::Correspondence> corrs =
381  correspondence_graph.FindTransitiveCorrespondences(
382  image_id, point2D_idx, kCorrTransitivity);
383 
384  std::unordered_set<point3D_t> point3D_ids;
385 
386  for (const auto corr : corrs) {
387  if (!reconstruction_->ExistsImage(corr.image_id)) {
388  std::cout << "[IncrementalMapper::RegisterNextImage] Image "
389  << corr.image_id << " does not exist" << std::endl;
390  continue;
391  }
392 
393  const Image& corr_image = reconstruction_->Image(corr.image_id);
394  if (!corr_image.IsRegistered()) {
395  continue;
396  }
397 
398  const Point2D& corr_point2D = corr_image.Point2D(corr.point2D_idx);
399  if (!corr_point2D.HasPoint3D()) {
400  continue;
401  }
402 
403  // Avoid duplicate correspondences.
404  if (point3D_ids.count(corr_point2D.Point3DId()) > 0) {
405  continue;
406  }
407 
408  const Camera& corr_camera =
409  reconstruction_->Camera(corr_image.CameraId());
410 
411  // Avoid correspondences to images with bogus camera parameters.
412  if (corr_camera.HasBogusParams(options.min_focal_length_ratio,
413  options.max_focal_length_ratio,
414  options.max_extra_param)) {
415  continue;
416  }
417 
418  const Point3D& point3D =
419  reconstruction_->Point3D(corr_point2D.Point3DId());
420 
421  tri_corrs.emplace_back(point2D_idx, corr_point2D.Point3DId());
422  point3D_ids.insert(corr_point2D.Point3DId());
423  tri_points2D.push_back(point2D.XY());
424  tri_points3D.push_back(point3D.XYZ());
425  }
426  }
427 
428  // The size of `next_image.num_tri_obs` and `tri_corrs_point2D_idxs.size()`
429  // can only differ, when there are images with bogus camera parameters, and
430  // hence we skip some of the 2D-3D correspondences.
431  if (tri_points2D.size() <
432  static_cast<size_t>(options.abs_pose_min_num_inliers)) {
433  return false;
434  }
435 
437  // 2D-3D estimation
439 
440  // Only refine / estimate focal length, if no focal length was specified
441  // (manually or through EXIF) and if it was not already estimated previously
442  // from another image (when multiple images share the same camera
443  // parameters)
444 
445  AbsolutePoseEstimationOptions abs_pose_options;
446  abs_pose_options.num_threads = options.num_threads;
447  abs_pose_options.num_focal_length_samples = 30;
448  abs_pose_options.min_focal_length_ratio = options.min_focal_length_ratio;
449  abs_pose_options.max_focal_length_ratio = options.max_focal_length_ratio;
450  abs_pose_options.ransac_options.max_error = options.abs_pose_max_error;
451  abs_pose_options.ransac_options.min_inlier_ratio =
453  // Use high confidence to avoid preemptive termination of P3P RANSAC
454  // - too early termination may lead to bad registration.
455  abs_pose_options.ransac_options.min_num_trials = 100;
456  abs_pose_options.ransac_options.max_num_trials = 10000;
457  abs_pose_options.ransac_options.confidence = 0.99999;
458 
459  AbsolutePoseRefinementOptions abs_pose_refinement_options;
460  if (num_reg_images_per_camera_[image.CameraId()] > 0) {
461  // Camera already refined from another image with the same camera.
462  if (camera.HasBogusParams(options.min_focal_length_ratio,
463  options.max_focal_length_ratio,
464  options.max_extra_param)) {
465  // Previously refined camera has bogus parameters,
466  // so reset parameters and try to re-estimage.
467  camera.SetParams(
468  database_cache_->Camera(image.CameraId()).Params());
469  abs_pose_options.estimate_focal_length =
470  !camera.HasPriorFocalLength();
471  abs_pose_refinement_options.refine_focal_length = true;
472  abs_pose_refinement_options.refine_extra_params = true;
473  } else {
474  abs_pose_options.estimate_focal_length = false;
475  abs_pose_refinement_options.refine_focal_length = false;
476  abs_pose_refinement_options.refine_extra_params = false;
477  }
478  } else {
479  // Camera not refined before. Note that the camera parameters might have
480  // been changed before but the image was filtered, so we explicitly
481  // reset the camera parameters and try to re-estimate them.
482  camera.SetParams(database_cache_->Camera(image.CameraId()).Params());
483  abs_pose_options.estimate_focal_length = !camera.HasPriorFocalLength();
484  abs_pose_refinement_options.refine_focal_length = true;
485  abs_pose_refinement_options.refine_extra_params = true;
486  }
487 
488  if (!options.abs_pose_refine_focal_length) {
489  abs_pose_options.estimate_focal_length = false;
490  abs_pose_refinement_options.refine_focal_length = false;
491  }
492 
493  if (!options.abs_pose_refine_extra_params) {
494  abs_pose_refinement_options.refine_extra_params = false;
495  }
496 
497  size_t num_inliers;
498  std::vector<char> inlier_mask;
499 
500  if (!EstimateAbsolutePose(abs_pose_options, tri_points2D, tri_points3D,
501  &image.Qvec(), &image.Tvec(), &camera,
502  &num_inliers, &inlier_mask)) {
503  return false;
504  }
505 
506  if (num_inliers < static_cast<size_t>(options.abs_pose_min_num_inliers)) {
507  return false;
508  }
509 
511  // Pose refinement
513 
514  if (!RefineAbsolutePose(abs_pose_refinement_options, inlier_mask,
515  tri_points2D, tri_points3D, &image.Qvec(),
516  &image.Tvec(), &camera)) {
517  return false;
518  }
519 
521  // Continue tracks
523 
524  reconstruction_->RegisterImage(image_id);
525  RegisterImageEvent(image_id);
526 
527  for (size_t i = 0; i < inlier_mask.size(); ++i) {
528  if (inlier_mask[i]) {
529  const point2D_t point2D_idx = tri_corrs[i].first;
530  const Point2D& point2D = image.Point2D(point2D_idx);
531  if (!point2D.HasPoint3D()) {
532  const point3D_t point3D_id = tri_corrs[i].second;
533  const TrackElement track_el(image_id, point2D_idx);
534  reconstruction_->AddObservation(point3D_id, track_el);
535  triangulator_->AddModifiedPoint3D(point3D_id);
536  }
537  }
538  }
539 
540  return true;
541 }
542 
544  const IncrementalTriangulator::Options& tri_options,
545  const image_t image_id) {
546  CHECK_NOTNULL(reconstruction_);
547  return triangulator_->TriangulateImage(tri_options, image_id);
548 }
549 
551  const IncrementalTriangulator::Options& tri_options) {
552  CHECK_NOTNULL(reconstruction_);
553  return triangulator_->Retriangulate(tri_options);
554 }
555 
557  const IncrementalTriangulator::Options& tri_options) {
558  CHECK_NOTNULL(reconstruction_);
559  return triangulator_->CompleteAllTracks(tri_options);
560 }
561 
563  const IncrementalTriangulator::Options& tri_options) {
564  CHECK_NOTNULL(reconstruction_);
565  return triangulator_->MergeAllTracks(tri_options);
566 }
567 
570  const Options& options,
571  const BundleAdjustmentOptions& ba_options,
572  const IncrementalTriangulator::Options& tri_options,
573  const image_t image_id,
574  const std::unordered_set<point3D_t>& point3D_ids) {
575  CHECK_NOTNULL(reconstruction_);
576  CHECK(options.Check());
577 
579 
580  // Find images that have most 3D points with given image in common.
581  const std::vector<image_t> local_bundle =
582  FindLocalBundle(options, image_id);
583 
584  // Do the bundle adjustment only if there is any connected images.
585  if (local_bundle.size() > 0) {
586  BundleAdjustmentConfig ba_config;
587  ba_config.AddImage(image_id);
588  for (const image_t local_image_id : local_bundle) {
589  ba_config.AddImage(local_image_id);
590  }
591 
592  // Fix the existing images, if option specified.
593  if (options.fix_existing_images) {
594  for (const image_t local_image_id : local_bundle) {
595  if (existing_image_ids_.count(local_image_id)) {
596  ba_config.SetConstantPose(local_image_id);
597  }
598  }
599  }
600 
601  // Determine which cameras to fix, when not all the registered images
602  // are within the current local bundle.
603  std::unordered_map<camera_t, size_t> num_images_per_camera;
604  for (const image_t image_id : ba_config.Images()) {
605  const Image& image = reconstruction_->Image(image_id);
606  num_images_per_camera[image.CameraId()] += 1;
607  }
608 
609  for (const auto& camera_id_and_num_images_pair :
610  num_images_per_camera) {
611  const size_t num_reg_images_for_camera =
612  num_reg_images_per_camera_.at(
613  camera_id_and_num_images_pair.first);
614  if (camera_id_and_num_images_pair.second <
615  num_reg_images_for_camera) {
616  ba_config.SetConstantCamera(
617  camera_id_and_num_images_pair.first);
618  }
619  }
620 
621  // Fix 7 DOF to avoid scale/rotation/translation drift in bundle
622  // adjustment.
623  if (local_bundle.size() == 1) {
624  ba_config.SetConstantPose(local_bundle[0]);
625  ba_config.SetConstantTvec(image_id, {0});
626  } else if (local_bundle.size() > 1) {
627  const image_t image_id1 = local_bundle[local_bundle.size() - 1];
628  const image_t image_id2 = local_bundle[local_bundle.size() - 2];
629  ba_config.SetConstantPose(image_id1);
630  if (!options.fix_existing_images ||
631  !existing_image_ids_.count(image_id2)) {
632  ba_config.SetConstantTvec(image_id2, {0});
633  }
634  }
635 
636  // Make sure, we refine all new and short-track 3D points, no matter if
637  // they are fully contained in the local image set or not. Do not
638  // include long track 3D points as they are usually already very stable
639  // and adding to them to bundle adjustment and track merging/completion
640  // would slow down the local bundle adjustment significantly.
641  std::unordered_set<point3D_t> variable_point3D_ids;
642  for (const point3D_t point3D_id : point3D_ids) {
643  const Point3D& point3D = reconstruction_->Point3D(point3D_id);
644  const size_t kMaxTrackLength = 15;
645  if (!point3D.HasError() ||
646  point3D.Track().Length() <= kMaxTrackLength) {
647  ba_config.AddVariablePoint(point3D_id);
648  variable_point3D_ids.insert(point3D_id);
649  }
650  }
651 
652  // Adjust the local bundle.
653  BundleAdjuster bundle_adjuster(ba_options, ba_config);
654  bundle_adjuster.Solve(reconstruction_);
655 
657  bundle_adjuster.Summary().num_residuals / 2;
658 
659  // Merge refined tracks with other existing points.
660  report.num_merged_observations =
661  triangulator_->MergeTracks(tri_options, variable_point3D_ids);
662  // Complete tracks that may have failed to triangulate before refinement
663  // of camera pose and calibration in bundle-adjustment. This may avoid
664  // that some points are filtered and it helps for subsequent image
665  // registrations.
666  report.num_completed_observations = triangulator_->CompleteTracks(
667  tri_options, variable_point3D_ids);
669  triangulator_->CompleteImage(tri_options, image_id);
670  }
671 
672  // Filter both the modified images and all changed 3D points to make sure
673  // there are no outlier points in the model. This results in duplicate work
674  // as many of the provided 3D points may also be contained in the adjusted
675  // images, but the filtering is not a bottleneck at this point.
676  std::unordered_set<image_t> filter_image_ids;
677  filter_image_ids.insert(image_id);
678  filter_image_ids.insert(local_bundle.begin(), local_bundle.end());
679  report.num_filtered_observations = reconstruction_->FilterPoints3DInImages(
681  filter_image_ids);
682  report.num_filtered_observations += reconstruction_->FilterPoints3D(
684  point3D_ids);
685 
686  return report;
687 }
688 
690  const Options& options, const BundleAdjustmentOptions& ba_options) {
691  CHECK_NOTNULL(reconstruction_);
692 
693  const std::vector<image_t>& reg_image_ids = reconstruction_->RegImageIds();
694 
695  CHECK_GE(reg_image_ids.size(), 2) << "At least two images must be "
696  "registered for global "
697  "bundle-adjustment";
698 
699  // Avoid degeneracies in bundle adjustment.
700  reconstruction_->FilterObservationsWithNegativeDepth();
701 
702  // Configure bundle adjustment.
703  BundleAdjustmentConfig ba_config;
704  for (const image_t image_id : reg_image_ids) {
705  ba_config.AddImage(image_id);
706  }
707 
708  // Fix the existing images, if option specified.
709  if (options.fix_existing_images) {
710  for (const image_t image_id : reg_image_ids) {
711  if (existing_image_ids_.count(image_id)) {
712  ba_config.SetConstantPose(image_id);
713  }
714  }
715  }
716 
717  // Fix 7-DOFs of the bundle adjustment problem.
718  ba_config.SetConstantPose(reg_image_ids[0]);
719  if (!options.fix_existing_images ||
720  !existing_image_ids_.count(reg_image_ids[1])) {
721  ba_config.SetConstantTvec(reg_image_ids[1], {0});
722  }
723 
724  // Run bundle adjustment.
725  BundleAdjuster bundle_adjuster(ba_options, ba_config);
726  if (!bundle_adjuster.Solve(reconstruction_)) {
727  return false;
728  }
729 
730  // Normalize scene for numerical stability and
731  // to avoid large scale changes in viewer.
732  reconstruction_->Normalize();
733 
734  return true;
735 }
736 
737 #ifdef PBA_ENABLED
738 bool IncrementalMapper::AdjustParallelGlobalBundle(
739  const BundleAdjustmentOptions& ba_options,
740  const ParallelBundleAdjuster::Options& parallel_ba_options) {
741  CHECK_NOTNULL(reconstruction_);
742 
743  const std::vector<image_t>& reg_image_ids = reconstruction_->RegImageIds();
744 
745  CHECK_GE(reg_image_ids.size(), 2)
746  << "At least two images must be registered for global "
747  "bundle-adjustment";
748 
749  // Avoid degeneracies in bundle adjustment.
750  reconstruction_->FilterObservationsWithNegativeDepth();
751 
752  // Configure bundle adjustment.
753  BundleAdjustmentConfig ba_config;
754  for (const image_t image_id : reg_image_ids) {
755  ba_config.AddImage(image_id);
756  }
757 
758  // Run bundle adjustment.
759  ParallelBundleAdjuster bundle_adjuster(parallel_ba_options, ba_options,
760  ba_config);
761  if (!bundle_adjuster.Solve(reconstruction_)) {
762  return false;
763  }
764 
765  // Normalize scene for numerical stability and
766  // to avoid large scale changes in viewer.
767  reconstruction_->Normalize();
768 
769  return true;
770 }
771 #endif
772 
773 size_t IncrementalMapper::FilterImages(const Options& options) {
774  CHECK_NOTNULL(reconstruction_);
775  CHECK(options.Check());
776 
777  // Do not filter images in the early stage of the reconstruction, since the
778  // calibration is often still refining a lot. Hence, the camera parameters
779  // are not stable in the beginning.
780  const size_t kMinNumImages = 20;
781  if (reconstruction_->NumRegImages() < kMinNumImages) {
782  return {};
783  }
784 
785  const std::vector<image_t> image_ids = reconstruction_->FilterImages(
787  options.max_extra_param);
788 
789  for (const image_t image_id : image_ids) {
790  DeRegisterImageEvent(image_id);
791  filtered_images_.insert(image_id);
792  }
793 
794  return image_ids.size();
795 }
796 
797 size_t IncrementalMapper::FilterPoints(const Options& options) {
798  CHECK_NOTNULL(reconstruction_);
799  CHECK(options.Check());
800  return reconstruction_->FilterAllPoints3D(options.filter_max_reproj_error,
801  options.filter_min_tri_angle);
802 }
803 
805  CHECK_NOTNULL(reconstruction_);
806  return *reconstruction_;
807 }
808 
810  return num_total_reg_images_;
811 }
812 
814  return num_shared_reg_images_;
815 }
816 
817 const std::unordered_set<point3D_t>& IncrementalMapper::GetModifiedPoints3D() {
818  return triangulator_->GetModifiedPoints3D();
819 }
820 
822  triangulator_->ClearModifiedPoints3D();
823 }
824 
825 std::vector<image_t> IncrementalMapper::FindFirstInitialImage(
826  const Options& options) const {
827  // Struct to hold meta-data for ranking images.
828  struct ImageInfo {
829  image_t image_id;
830  bool prior_focal_length;
831  image_t num_correspondences;
832  };
833 
834  const size_t init_max_reg_trials =
835  static_cast<size_t>(options.init_max_reg_trials);
836 
837  // Collect information of all not yet registered images with
838  // correspondences.
839  std::vector<ImageInfo> image_infos;
840  image_infos.reserve(reconstruction_->NumImages());
841  for (const auto& image : reconstruction_->Images()) {
842  // Only images with correspondences can be registered.
843  if (image.second.NumCorrespondences() == 0) {
844  continue;
845  }
846 
847  // Only use images for initialization a maximum number of times.
848  if (init_num_reg_trials_.count(image.first) &&
849  init_num_reg_trials_.at(image.first) >= init_max_reg_trials) {
850  continue;
851  }
852 
853  // Only use images for initialization that are not registered in any
854  // of the other reconstructions.
855  if (num_registrations_.count(image.first) > 0 &&
856  num_registrations_.at(image.first) > 0) {
857  continue;
858  }
859 
860  const class Camera& camera =
861  reconstruction_->Camera(image.second.CameraId());
862  ImageInfo image_info;
863  image_info.image_id = image.first;
864  image_info.prior_focal_length = camera.HasPriorFocalLength();
865  image_info.num_correspondences = image.second.NumCorrespondences();
866  image_infos.push_back(image_info);
867  }
868 
869  // Sort images such that images with a prior focal length and more
870  // correspondences are preferred, i.e. they appear in the front of the list.
871  std::sort(image_infos.begin(), image_infos.end(),
872  [](const ImageInfo& image_info1, const ImageInfo& image_info2) {
873  if (image_info1.prior_focal_length &&
874  !image_info2.prior_focal_length) {
875  return true;
876  } else if (!image_info1.prior_focal_length &&
877  image_info2.prior_focal_length) {
878  return false;
879  } else {
880  return image_info1.num_correspondences >
881  image_info2.num_correspondences;
882  }
883  });
884 
885  // Extract image identifiers in sorted order.
886  std::vector<image_t> image_ids;
887  image_ids.reserve(image_infos.size());
888  for (const ImageInfo& image_info : image_infos) {
889  image_ids.push_back(image_info.image_id);
890  }
891 
892  return image_ids;
893 }
894 
895 std::vector<image_t> IncrementalMapper::FindSecondInitialImage(
896  const Options& options, const image_t image_id1) const {
897  const CorrespondenceGraph& correspondence_graph =
898  database_cache_->CorrespondenceGraph();
899 
900  // Collect images that are connected to the first seed image and have
901  // not been registered before in other reconstructions.
902  const class Image& image1 = reconstruction_->Image(image_id1);
903  std::unordered_map<image_t, point2D_t> num_correspondences;
904  for (point2D_t point2D_idx = 0; point2D_idx < image1.NumPoints2D();
905  ++point2D_idx) {
906  for (const auto& corr :
907  correspondence_graph.FindCorrespondences(image_id1, point2D_idx)) {
908  if (num_registrations_.count(corr.image_id) == 0 ||
909  num_registrations_.at(corr.image_id) == 0) {
910  num_correspondences[corr.image_id] += 1;
911  }
912  }
913  }
914 
915  // Struct to hold meta-data for ranking images.
916  struct ImageInfo {
917  image_t image_id;
918  bool prior_focal_length;
919  point2D_t num_correspondences;
920  };
921 
922  const size_t init_min_num_inliers =
923  static_cast<size_t>(options.init_min_num_inliers);
924 
925  // Compose image information in a compact form for sorting.
926  std::vector<ImageInfo> image_infos;
927  image_infos.reserve(reconstruction_->NumImages());
928  for (const auto elem : num_correspondences) {
929  if (elem.second >= init_min_num_inliers) {
930  const class Image& image = reconstruction_->Image(elem.first);
931  const class Camera& camera =
932  reconstruction_->Camera(image.CameraId());
933  ImageInfo image_info;
934  image_info.image_id = elem.first;
935  image_info.prior_focal_length = camera.HasPriorFocalLength();
936  image_info.num_correspondences = elem.second;
937  image_infos.push_back(image_info);
938  }
939  }
940 
941  // Sort images such that images with a prior focal length and more
942  // correspondences are preferred, i.e. they appear in the front of the list.
943  std::sort(image_infos.begin(), image_infos.end(),
944  [](const ImageInfo& image_info1, const ImageInfo& image_info2) {
945  if (image_info1.prior_focal_length &&
946  !image_info2.prior_focal_length) {
947  return true;
948  } else if (!image_info1.prior_focal_length &&
949  image_info2.prior_focal_length) {
950  return false;
951  } else {
952  return image_info1.num_correspondences >
953  image_info2.num_correspondences;
954  }
955  });
956 
957  // Extract image identifiers in sorted order.
958  std::vector<image_t> image_ids;
959  image_ids.reserve(image_infos.size());
960  for (const ImageInfo& image_info : image_infos) {
961  image_ids.push_back(image_info.image_id);
962  }
963 
964  return image_ids;
965 }
966 
967 std::vector<image_t> IncrementalMapper::FindLocalBundle(
968  const Options& options, const image_t image_id) const {
969  CHECK(options.Check());
970 
971  const Image& image = reconstruction_->Image(image_id);
972  CHECK(image.IsRegistered());
973 
974  // Extract all images that have at least one 3D point with the query image
975  // in common, and simultaneously count the number of common 3D points.
976 
977  std::unordered_map<image_t, size_t> shared_observations;
978 
979  std::unordered_set<point3D_t> point3D_ids;
980  point3D_ids.reserve(image.NumPoints3D());
981 
982  for (const Point2D& point2D : image.Points2D()) {
983  if (point2D.HasPoint3D()) {
984  point3D_ids.insert(point2D.Point3DId());
985  const Point3D& point3D =
986  reconstruction_->Point3D(point2D.Point3DId());
987  for (const TrackElement& track_el : point3D.Track().Elements()) {
988  if (track_el.image_id != image_id) {
989  shared_observations[track_el.image_id] += 1;
990  }
991  }
992  }
993  }
994 
995  // Sort overlapping images according to number of shared observations.
996 
997  std::vector<std::pair<image_t, size_t>> overlapping_images(
998  shared_observations.begin(), shared_observations.end());
999  std::sort(overlapping_images.begin(), overlapping_images.end(),
1000  [](const std::pair<image_t, size_t>& image1,
1001  const std::pair<image_t, size_t>& image2) {
1002  return image1.second > image2.second;
1003  });
1004 
1005  // The local bundle is composed of the given image and its most connected
1006  // neighbor images, hence the subtraction of 1.
1007 
1008  const size_t num_images =
1009  static_cast<size_t>(options.local_ba_num_images - 1);
1010  const size_t num_eff_images =
1011  std::min(num_images, overlapping_images.size());
1012 
1013  // Extract most connected images and ensure sufficient triangulation angle.
1014 
1015  std::vector<image_t> local_bundle_image_ids;
1016  local_bundle_image_ids.reserve(num_eff_images);
1017 
1018  // If the number of overlapping images equals the number of desired images
1019  // in the local bundle, then simply copy over the image identifiers.
1020  if (overlapping_images.size() == num_eff_images) {
1021  for (const auto& overlapping_image : overlapping_images) {
1022  local_bundle_image_ids.push_back(overlapping_image.first);
1023  }
1024  return local_bundle_image_ids;
1025  }
1026 
1027  // In the following iteration, we start with the most overlapping images and
1028  // check whether it has sufficient triangulation angle. If none of the
1029  // overlapping images has sufficient triangulation angle, we relax the
1030  // triangulation angle threshold and start from the most overlapping image
1031  // again. In the end, if we still haven't found enough images, we simply use
1032  // the most overlapping images.
1033 
1034  const double min_tri_angle_rad = DegToRad(options.local_ba_min_tri_angle);
1035 
1036  // The selection thresholds (minimum triangulation angle, minimum number of
1037  // shared observations), which are successively relaxed.
1038  const std::array<std::pair<double, double>, 8> selection_thresholds = {{
1039  std::make_pair(min_tri_angle_rad / 1.0, 0.6 * image.NumPoints3D()),
1040  std::make_pair(min_tri_angle_rad / 1.5, 0.6 * image.NumPoints3D()),
1041  std::make_pair(min_tri_angle_rad / 2.0, 0.5 * image.NumPoints3D()),
1042  std::make_pair(min_tri_angle_rad / 2.5, 0.4 * image.NumPoints3D()),
1043  std::make_pair(min_tri_angle_rad / 3.0, 0.3 * image.NumPoints3D()),
1044  std::make_pair(min_tri_angle_rad / 4.0, 0.2 * image.NumPoints3D()),
1045  std::make_pair(min_tri_angle_rad / 5.0, 0.1 * image.NumPoints3D()),
1046  std::make_pair(min_tri_angle_rad / 6.0, 0.1 * image.NumPoints3D()),
1047  }};
1048 
1049  const Eigen::Vector3d proj_center = image.ProjectionCenter();
1050  std::vector<Eigen::Vector3d> shared_points3D;
1051  shared_points3D.reserve(image.NumPoints3D());
1052  std::vector<double> tri_angles(overlapping_images.size(), -1.0);
1053  std::vector<char> used_overlapping_images(overlapping_images.size(), false);
1054 
1055  for (const auto& selection_threshold : selection_thresholds) {
1056  for (size_t overlapping_image_idx = 0;
1057  overlapping_image_idx < overlapping_images.size();
1058  ++overlapping_image_idx) {
1059  // Check if the image has sufficient overlap. Since the images are
1060  // ordered based on the overlap, we can just skip the remaining
1061  // ones.
1062  if (overlapping_images[overlapping_image_idx].second <
1063  selection_threshold.second) {
1064  break;
1065  }
1066 
1067  // Check if the image is already in the local bundle.
1068  if (used_overlapping_images[overlapping_image_idx]) {
1069  continue;
1070  }
1071 
1072  const auto& overlapping_image = reconstruction_->Image(
1073  overlapping_images[overlapping_image_idx].first);
1074  const Eigen::Vector3d overlapping_proj_center =
1075  overlapping_image.ProjectionCenter();
1076 
1077  // In the first iteration, compute the triangulation angle. In later
1078  // iterations, reuse the previously computed value.
1079  double& tri_angle = tri_angles[overlapping_image_idx];
1080  if (tri_angle < 0.0) {
1081  // Collect the commonly observed 3D points.
1082  shared_points3D.clear();
1083  for (const Point2D& point2D : image.Points2D()) {
1084  if (point2D.HasPoint3D() &&
1085  point3D_ids.count(point2D.Point3DId())) {
1086  shared_points3D.push_back(
1087  reconstruction_->Point3D(point2D.Point3DId())
1088  .XYZ());
1089  }
1090  }
1091 
1092  // Calculate the triangulation angle at a certain percentile.
1093  const double kTriangulationAnglePercentile = 75;
1094  tri_angle =
1096  proj_center, overlapping_proj_center,
1097  shared_points3D),
1098  kTriangulationAnglePercentile);
1099  }
1100 
1101  // Check that the image has sufficient triangulation angle.
1102  if (tri_angle >= selection_threshold.first) {
1103  local_bundle_image_ids.push_back(overlapping_image.ImageId());
1104  used_overlapping_images[overlapping_image_idx] = true;
1105  // Check if we already collected enough images.
1106  if (local_bundle_image_ids.size() >= num_eff_images) {
1107  break;
1108  }
1109  }
1110  }
1111 
1112  // Check if we already collected enough images.
1113  if (local_bundle_image_ids.size() >= num_eff_images) {
1114  break;
1115  }
1116  }
1117 
1118  // In case there are not enough images with sufficient triangulation angle,
1119  // simply fill up the rest with the most overlapping images.
1120 
1121  if (local_bundle_image_ids.size() < num_eff_images) {
1122  for (size_t overlapping_image_idx = 0;
1123  overlapping_image_idx < overlapping_images.size();
1124  ++overlapping_image_idx) {
1125  // Collect image if it is not yet in the local bundle.
1126  if (!used_overlapping_images[overlapping_image_idx]) {
1127  local_bundle_image_ids.push_back(
1128  overlapping_images[overlapping_image_idx].first);
1129  used_overlapping_images[overlapping_image_idx] = true;
1130 
1131  // Check if we already collected enough images.
1132  if (local_bundle_image_ids.size() >= num_eff_images) {
1133  break;
1134  }
1135  }
1136  }
1137  }
1138 
1139  return local_bundle_image_ids;
1140 }
1141 
1142 void IncrementalMapper::RegisterImageEvent(const image_t image_id) {
1143  const Image& image = reconstruction_->Image(image_id);
1144  size_t& num_reg_images_for_camera =
1145  num_reg_images_per_camera_[image.CameraId()];
1146  num_reg_images_for_camera += 1;
1147 
1148  size_t& num_regs_for_image = num_registrations_[image_id];
1149  num_regs_for_image += 1;
1150  if (num_regs_for_image == 1) {
1151  num_total_reg_images_ += 1;
1152  } else if (num_regs_for_image > 1) {
1153  num_shared_reg_images_ += 1;
1154  }
1155 }
1156 
1157 void IncrementalMapper::DeRegisterImageEvent(const image_t image_id) {
1158  const Image& image = reconstruction_->Image(image_id);
1159  size_t& num_reg_images_for_camera =
1160  num_reg_images_per_camera_.at(image.CameraId());
1161  CHECK_GT(num_reg_images_for_camera, 0);
1162  num_reg_images_for_camera -= 1;
1163 
1164  size_t& num_regs_for_image = num_registrations_[image_id];
1165  num_regs_for_image -= 1;
1166  if (num_regs_for_image == 0) {
1167  num_total_reg_images_ -= 1;
1168  } else if (num_regs_for_image > 0) {
1169  num_shared_reg_images_ -= 1;
1170  }
1171 }
1172 
1173 bool IncrementalMapper::EstimateInitialTwoViewGeometry(
1174  const Options& options,
1175  const image_t image_id1,
1176  const image_t image_id2) {
1177  const image_pair_t image_pair_id =
1178  Database::ImagePairToPairId(image_id1, image_id2);
1179 
1180  if (prev_init_image_pair_id_ == image_pair_id) {
1181  return true;
1182  }
1183 
1184  const Image& image1 = database_cache_->Image(image_id1);
1185  const Camera& camera1 = database_cache_->Camera(image1.CameraId());
1186 
1187  const Image& image2 = database_cache_->Image(image_id2);
1188  const Camera& camera2 = database_cache_->Camera(image2.CameraId());
1189 
1190  const CorrespondenceGraph& correspondence_graph =
1191  database_cache_->CorrespondenceGraph();
1192  const FeatureMatches matches =
1193  correspondence_graph.FindCorrespondencesBetweenImages(image_id1,
1194  image_id2);
1195 
1196  std::vector<Eigen::Vector2d> points1;
1197  points1.reserve(image1.NumPoints2D());
1198  for (const auto& point : image1.Points2D()) {
1199  points1.push_back(point.XY());
1200  }
1201 
1202  std::vector<Eigen::Vector2d> points2;
1203  points2.reserve(image2.NumPoints2D());
1204  for (const auto& point : image2.Points2D()) {
1205  points2.push_back(point.XY());
1206  }
1207 
1208  TwoViewGeometry two_view_geometry;
1209  TwoViewGeometry::Options two_view_geometry_options;
1210  two_view_geometry_options.ransac_options.min_num_trials = 30;
1211  two_view_geometry_options.ransac_options.max_error = options.init_max_error;
1212  two_view_geometry.EstimateCalibrated(camera1, points1, camera2, points2,
1213  matches, two_view_geometry_options);
1214 
1215  if (!two_view_geometry.EstimateRelativePose(camera1, points1, camera2,
1216  points2)) {
1217  return false;
1218  }
1219 
1220  if (static_cast<int>(two_view_geometry.inlier_matches.size()) >=
1221  options.init_min_num_inliers &&
1222  std::abs(two_view_geometry.tvec.z()) <
1223  options.init_max_forward_motion &&
1224  two_view_geometry.tri_angle > DegToRad(options.init_min_tri_angle)) {
1225  prev_init_image_pair_id_ = image_pair_id;
1226  prev_init_two_view_geometry_ = two_view_geometry;
1227  return true;
1228  }
1229 
1230  return false;
1231 }
1232 
1233 } // namespace colmap
std::shared_ptr< core::Tensor > image
Point3D(T x=0., T y=0., T z=0.)
Definition: Common.h:120
const ceres::Solver::Summary & Summary() const
bool Solve(Reconstruction *reconstruction)
void AddVariablePoint(const point3D_t point3D_id)
const std::unordered_set< image_t > & Images() const
void SetConstantCamera(const camera_t camera_id)
void SetConstantPose(const image_t image_id)
void AddImage(const image_t image_id)
void SetConstantTvec(const image_t image_id, const std::vector< int > &idxs)
Eigen::Vector2d ImageToWorld(const Eigen::Vector2d &image_point) const
Definition: camera.cc:219
const std::vector< double > & Params() const
Definition: camera.h:176
void SetParams(const std::vector< double > &params)
Definition: camera.h:188
bool HasPriorFocalLength() const
Definition: camera.h:168
bool HasBogusParams(const double min_focal_length_ratio, const double max_focal_length_ratio, const double max_extra_param) const
Definition: camera.cc:186
FeatureMatches FindCorrespondencesBetweenImages(const image_t image_id1, const image_t image_id2) const
std::vector< Correspondence > FindTransitiveCorrespondences(const image_t image_id, const point2D_t point2D_idx, const size_t transitivity) const
class Camera & Camera(const camera_t camera_id)
const class CorrespondenceGraph & CorrespondenceGraph() const
bool ExistsImage(const image_t image_id) const
static image_pair_t ImagePairToPairId(const image_t image_id1, const image_t image_id2)
Definition: database.h:339
Eigen::Matrix3x4d ProjectionMatrix() const
Definition: image.cc:140
const Eigen::Vector3d & Tvec() const
Definition: image.h:325
const class Point2D & Point2D(const point2D_t point2D_idx) const
Definition: image.h:349
const Eigen::Vector4d & Qvec() const
Definition: image.h:301
bool IsRegistered() const
Definition: image.h:257
Eigen::Vector3d ProjectionCenter() const
Definition: image.cc:152
camera_t CameraId() const
Definition: image.h:248
LocalBundleAdjustmentReport AdjustLocalBundle(const Options &options, const BundleAdjustmentOptions &ba_options, const IncrementalTriangulator::Options &tri_options, const image_t image_id, const std::unordered_set< point3D_t > &point3D_ids)
bool FindInitialImagePair(const Options &options, image_t *image_id1, image_t *image_id2)
void EndReconstruction(const bool discard)
size_t MergeTracks(const IncrementalTriangulator::Options &tri_options)
void BeginReconstruction(Reconstruction *reconstruction)
bool RegisterNextImage(const Options &options, const image_t image_id)
const std::unordered_set< point3D_t > & GetModifiedPoints3D()
bool RegisterInitialImagePair(const Options &options, const image_t image_id1, const image_t image_id2)
size_t Retriangulate(const IncrementalTriangulator::Options &tri_options)
bool AdjustGlobalBundle(const Options &options, const BundleAdjustmentOptions &ba_options)
size_t FilterImages(const Options &options)
std::vector< image_t > FindNextImages(const Options &options)
size_t TriangulateImage(const IncrementalTriangulator::Options &tri_options, const image_t image_id)
const Reconstruction & GetReconstruction() const
size_t CompleteTracks(const IncrementalTriangulator::Options &tri_options)
IncrementalMapper(const DatabaseCache *database_cache)
size_t FilterPoints(const Options &options)
point3D_t Point3DId() const
Definition: point2d.h:63
bool HasPoint3D() const
Definition: point2d.h:65
const Eigen::Vector2d & XY() const
Definition: point2d.h:53
const Eigen::Vector3d & XYZ() const
Definition: point3d.h:74
const class Track & Track() const
Definition: point3d.h:106
bool HasError() const
Definition: point3d.h:102
size_t NumImages() const
size_t FilterAllPoints3D(const double max_reproj_error, const double min_tri_angle)
size_t NumRegImages() const
size_t FilterObservationsWithNegativeDepth()
void Normalize(const double extent=10.0, const double p0=0.1, const double p1=0.9, const bool use_images=true)
const std::unordered_map< image_t, class Image > & Images() const
std::vector< image_t > FilterImages(const double min_focal_length_ratio, const double max_focal_length_ratio, const double max_extra_param)
const class Image & Image(const image_t image_id) const
const class Camera & Camera(const camera_t camera_id) const
void Load(const DatabaseCache &database_cache)
void AddObservation(const point3D_t point3D_id, const TrackElement &track_el)
const std::vector< image_t > & RegImageIds() const
size_t FilterPoints3D(const double max_reproj_error, const double min_tri_angle, const std::unordered_set< point3D_t > &point3D_ids)
bool ExistsImage(const image_t image_id) const
void RegisterImage(const image_t image_id)
size_t FilterPoints3DInImages(const double max_reproj_error, const double min_tri_angle, const std::unordered_set< image_t > &image_ids)
const class Point3D & Point3D(const point3D_t point3D_id) const
point3D_t AddPoint3D(const Eigen::Vector3d &xyz, const Track &track, const Eigen::Vector3ub &color=Eigen::Vector3ub::Zero())
void SetUp(const CorrespondenceGraph *correspondence_graph)
void AddElement(const TrackElement &element)
Definition: track.h:103
size_t Length() const
Definition: track.h:82
void Reserve(const size_t num_elements)
Definition: track.h:120
const TrackElement & Element(const size_t idx) const
Definition: track.h:93
#define CHECK_OPTION_LE(val1, val2)
Definition: logging.h:31
#define CHECK_OPTION_GT(val1, val2)
Definition: logging.h:34
#define CHECK_OPTION_GE(val1, val2)
Definition: logging.h:33
float
Matrix< double, 3, 4 > Matrix3x4d
Definition: types.h:39
QTextStream & endl(QTextStream &stream)
Definition: QtCompat.h:718
CLOUDVIEWER_HOST_DEVICE Pair< First, Second > make_pair(const First &_first, const Second &_second)
Definition: SlabTraits.h:49
uint64_t image_pair_t
Definition: types.h:64
bool HasPointPositiveDepth(const Eigen::Matrix3x4d &proj_matrix, const Eigen::Vector3d &point3D)
Definition: projection.cc:191
uint64_t point3D_t
Definition: types.h:72
Eigen::Vector4d ComposeIdentityQuaternion()
Definition: pose.h:209
std::vector< double > CalculateTriangulationAngles(const Eigen::Vector3d &proj_center1, const Eigen::Vector3d &proj_center2, const std::vector< Eigen::Vector3d > &points3D)
uint32_t point2D_t
Definition: types.h:67
bool EstimateAbsolutePose(const AbsolutePoseEstimationOptions &options, const std::vector< Eigen::Vector2d > &points2D, const std::vector< Eigen::Vector3d > &points3D, Eigen::Vector4d *qvec, Eigen::Vector3d *tvec, Camera *camera, size_t *num_inliers, std::vector< char > *inlier_mask)
Definition: pose.cc:79
Eigen::Vector3d TriangulatePoint(const Eigen::Matrix3x4d &proj_matrix1, const Eigen::Matrix3x4d &proj_matrix2, const Eigen::Vector2d &point1, const Eigen::Vector2d &point2)
const image_pair_t kInvalidImagePairId
Definition: types.h:77
uint32_t image_t
Definition: types.h:61
bool RefineAbsolutePose(const AbsolutePoseRefinementOptions &options, const std::vector< char > &inlier_mask, const std::vector< Eigen::Vector2d > &points2D, const std::vector< Eigen::Vector3d > &points3D, Eigen::Vector4d *qvec, Eigen::Vector3d *tvec, Camera *camera)
Definition: pose.cc:203
const image_t kInvalidImageId
Definition: types.h:76
double CalculateTriangulationAngle(const Eigen::Vector3d &proj_center1, const Eigen::Vector3d &proj_center2, const Eigen::Vector3d &point3D)
float DegToRad(const float deg)
Definition: math.h:171
std::vector< FeatureMatch > FeatureMatches
Definition: types.h:80
T Percentile(const std::vector< T > &elems, const double p)
Definition: math.h:209
ImageSelectionMethod image_selection_method
size_t min_num_trials
Definition: ransac.h:40
double min_inlier_ratio
Definition: ransac.h:29
size_t max_num_trials
Definition: ransac.h:41
image_t image_id
Definition: track.h:22
point2D_t point2D_idx
Definition: track.h:24