ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
incremental_triangulator.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 
33 
34 #include "base/projection.h"
36 #include "util/misc.h"
37 
38 namespace colmap {
39 
52  return true;
53 }
54 
56  const CorrespondenceGraph* correspondence_graph,
57  Reconstruction* reconstruction)
58  : correspondence_graph_(correspondence_graph),
59  reconstruction_(reconstruction) {}
60 
62  const image_t image_id) {
63  CHECK(options.Check());
64 
65  size_t num_tris = 0;
66 
67  ClearCaches();
68 
69  if (!reconstruction_->ExistsImage(image_id)) {
70  std::cout << "[IncrementalTriangulator::TriangulateImage] Image "
71  << image_id << " does not exist" << std::endl;
72  return num_tris;
73  }
74 
75  const Image& image = reconstruction_->Image(image_id);
76  if (!image.IsRegistered()) {
77  return num_tris;
78  }
79 
80  const Camera& camera = reconstruction_->Camera(image.CameraId());
81  if (HasCameraBogusParams(options, camera)) {
82  return num_tris;
83  }
84 
85  // Correspondence data for reference observation in given image. We iterate
86  // over all observations of the image and each observation once becomes
87  // the reference correspondence.
88  CorrData ref_corr_data;
89  ref_corr_data.image_id = image_id;
90  ref_corr_data.image = &image;
91  ref_corr_data.camera = &camera;
92 
93  // Container for correspondences from reference observation to other images.
94  std::vector<CorrData> corrs_data;
95 
96  // Try to triangulate all image observations.
97  for (point2D_t point2D_idx = 0; point2D_idx < image.NumPoints2D();
98  ++point2D_idx) {
99  const size_t num_triangulated = Find(
100  options, image_id, point2D_idx,
101  static_cast<size_t>(options.max_transitivity), &corrs_data);
102  if (corrs_data.empty()) {
103  continue;
104  }
105 
106  const Point2D& point2D = image.Point2D(point2D_idx);
107  ref_corr_data.point2D_idx = point2D_idx;
108  ref_corr_data.point2D = &point2D;
109 
110  if (num_triangulated == 0) {
111  corrs_data.push_back(ref_corr_data);
112  num_tris += Create(options, corrs_data);
113  } else {
114  // Continue correspondences to existing 3D points.
115  num_tris += Continue(options, ref_corr_data, corrs_data);
116  // Create points from correspondences that are not continued.
117  corrs_data.push_back(ref_corr_data);
118  num_tris += Create(options, corrs_data);
119  }
120  }
121 
122  return num_tris;
123 }
124 
126  const image_t image_id) {
127  CHECK(options.Check());
128 
129  size_t num_tris = 0;
130 
131  ClearCaches();
132 
133  if (!reconstruction_->ExistsImage(image_id)) {
134  std::cout << "[IncrementalTriangulator::CompleteImage] Image "
135  << image_id << " does not exist" << std::endl;
136  return num_tris;
137  }
138 
139  const Image& image = reconstruction_->Image(image_id);
140  if (!image.IsRegistered()) {
141  return num_tris;
142  }
143 
144  const Camera& camera = reconstruction_->Camera(image.CameraId());
145  if (HasCameraBogusParams(options, camera)) {
146  return num_tris;
147  }
148 
149  // Setup estimation options.
150  EstimateTriangulationOptions tri_options;
151  tri_options.min_tri_angle = DegToRad(options.min_angle);
152  tri_options.residual_type =
154  tri_options.ransac_options.max_error = options.complete_max_reproj_error;
155  tri_options.ransac_options.confidence = 0.9999;
156  tri_options.ransac_options.min_inlier_ratio = 0.02;
157  tri_options.ransac_options.max_num_trials = 10000;
158 
159  // Correspondence data for reference observation in given image. We iterate
160  // over all observations of the image and each observation once becomes
161  // the reference correspondence.
162  CorrData ref_corr_data;
163  ref_corr_data.image_id = image_id;
164  ref_corr_data.image = &image;
165  ref_corr_data.camera = &camera;
166 
167  // Container for correspondences from reference observation to other images.
168  std::vector<CorrData> corrs_data;
169 
170  for (point2D_t point2D_idx = 0; point2D_idx < image.NumPoints2D();
171  ++point2D_idx) {
172  const Point2D& point2D = image.Point2D(point2D_idx);
173  if (point2D.HasPoint3D()) {
174  // Complete existing track.
175  num_tris += Complete(options, point2D.Point3DId());
176  continue;
177  }
178 
179  if (options.ignore_two_view_tracks &&
180  correspondence_graph_->IsTwoViewObservation(image_id,
181  point2D_idx)) {
182  continue;
183  }
184 
185  const size_t num_triangulated = Find(
186  options, image_id, point2D_idx,
187  static_cast<size_t>(options.max_transitivity), &corrs_data);
188  if (num_triangulated || corrs_data.empty()) {
189  continue;
190  }
191 
192  ref_corr_data.point2D = &point2D;
193  ref_corr_data.point2D_idx = point2D_idx;
194  corrs_data.push_back(ref_corr_data);
195 
196  // Setup data for triangulation estimation.
197  std::vector<TriangulationEstimator::PointData> point_data;
198  point_data.resize(corrs_data.size());
199  std::vector<TriangulationEstimator::PoseData> pose_data;
200  pose_data.resize(corrs_data.size());
201  for (size_t i = 0; i < corrs_data.size(); ++i) {
202  const CorrData& corr_data = corrs_data[i];
203  point_data[i].point = corr_data.point2D->XY();
204  point_data[i].point_normalized =
205  corr_data.camera->ImageToWorld(point_data[i].point);
206  pose_data[i].proj_matrix = corr_data.image->ProjectionMatrix();
207  pose_data[i].proj_center = corr_data.image->ProjectionCenter();
208  pose_data[i].camera = corr_data.camera;
209  }
210 
211  // Enforce exhaustive sampling for small track lengths.
212  const size_t kExhaustiveSamplingThreshold = 15;
213  if (point_data.size() <= kExhaustiveSamplingThreshold) {
214  tri_options.ransac_options.min_num_trials =
215  NChooseK(point_data.size(), 2);
216  }
217 
218  // Estimate triangulation.
219  Eigen::Vector3d xyz;
220  std::vector<char> inlier_mask;
221  if (!EstimateTriangulation(tri_options, point_data, pose_data,
222  &inlier_mask, &xyz)) {
223  continue;
224  }
225 
226  // Add inliers to estimated track.
227  Track track;
228  track.Reserve(corrs_data.size());
229  for (size_t i = 0; i < inlier_mask.size(); ++i) {
230  if (inlier_mask[i]) {
231  const CorrData& corr_data = corrs_data[i];
232  track.AddElement(corr_data.image_id, corr_data.point2D_idx);
233  num_tris += 1;
234  }
235  }
236 
237  const point3D_t point3D_id = reconstruction_->AddPoint3D(xyz, track);
238  modified_point3D_ids_.insert(point3D_id);
239  }
240 
241  return num_tris;
242 }
243 
245  const Options& options,
246  const std::unordered_set<point3D_t>& point3D_ids) {
247  CHECK(options.Check());
248 
249  size_t num_completed = 0;
250 
251  ClearCaches();
252 
253  for (const point3D_t point3D_id : point3D_ids) {
254  num_completed += Complete(options, point3D_id);
255  }
256 
257  return num_completed;
258 }
259 
261  CHECK(options.Check());
262 
263  size_t num_completed = 0;
264 
265  ClearCaches();
266 
267  for (const point3D_t point3D_id : reconstruction_->Point3DIds()) {
268  num_completed += Complete(options, point3D_id);
269  }
270 
271  return num_completed;
272 }
273 
275  const Options& options,
276  const std::unordered_set<point3D_t>& point3D_ids) {
277  CHECK(options.Check());
278 
279  size_t num_merged = 0;
280 
281  ClearCaches();
282 
283  for (const point3D_t point3D_id : point3D_ids) {
284  num_merged += Merge(options, point3D_id);
285  }
286 
287  return num_merged;
288 }
289 
291  CHECK(options.Check());
292 
293  size_t num_merged = 0;
294 
295  ClearCaches();
296 
297  for (const point3D_t point3D_id : reconstruction_->Point3DIds()) {
298  num_merged += Merge(options, point3D_id);
299  }
300 
301  return num_merged;
302 }
303 
305  CHECK(options.Check());
306 
307  size_t num_tris = 0;
308 
309  ClearCaches();
310 
311  Options re_options = options;
312  re_options.continue_max_angle_error = options.re_max_angle_error;
313 
314  for (const auto& image_pair : reconstruction_->ImagePairs()) {
315  // Only perform retriangulation for under-reconstructed image pairs.
316  const double tri_ratio =
317  static_cast<double>(image_pair.second.num_tri_corrs) /
318  static_cast<double>(image_pair.second.num_total_corrs);
319  if (tri_ratio >= options.re_min_ratio) {
320  continue;
321  }
322 
323  // Check if images are registered yet.
324 
325  image_t image_id1;
326  image_t image_id2;
327  Database::PairIdToImagePair(image_pair.first, &image_id1, &image_id2);
328 
329  if (!reconstruction_->ExistsImage(image_id1) ||
330  !reconstruction_->ExistsImage(image_id2)) {
331  std::cout << "[IncrementalTriangulator::Retriangulate] Image "
332  << image_id1 << " or " << image_id2 << " does not exist"
333  << std::endl;
334  continue;
335  }
336 
337  const Image& image1 = reconstruction_->Image(image_id1);
338  if (!image1.IsRegistered()) {
339  continue;
340  }
341 
342  const Image& image2 = reconstruction_->Image(image_id2);
343  if (!image2.IsRegistered()) {
344  continue;
345  }
346 
347  // Only perform retriangulation for a maximum number of trials.
348 
349  int& num_re_trials = re_num_trials_[image_pair.first];
350  if (num_re_trials >= options.re_max_trials) {
351  continue;
352  }
353  num_re_trials += 1;
354 
355  const Camera& camera1 = reconstruction_->Camera(image1.CameraId());
356  const Camera& camera2 = reconstruction_->Camera(image2.CameraId());
357  if (HasCameraBogusParams(options, camera1) ||
358  HasCameraBogusParams(options, camera2)) {
359  continue;
360  }
361 
362  // Find correspondences and perform retriangulation.
363 
364  const FeatureMatches& corrs =
365  correspondence_graph_->FindCorrespondencesBetweenImages(
366  image_id1, image_id2);
367 
368  for (const auto& corr : corrs) {
369  const Point2D& point2D1 = image1.Point2D(corr.point2D_idx1);
370  const Point2D& point2D2 = image2.Point2D(corr.point2D_idx2);
371 
372  // Two cases are possible here: both points belong to the same 3D
373  // point or to different 3D points. In the former case, there is
374  // nothing to do. In the latter case, we do not attempt
375  // retriangulation, as retriangulated correspondences are very
376  // likely bogus and would therefore destroy both 3D points if
377  // merged.
378  if (point2D1.HasPoint3D() && point2D2.HasPoint3D()) {
379  continue;
380  }
381 
382  CorrData corr_data1;
383  corr_data1.image_id = image_id1;
384  corr_data1.point2D_idx = corr.point2D_idx1;
385  corr_data1.image = &image1;
386  corr_data1.camera = &camera1;
387  corr_data1.point2D = &point2D1;
388 
389  CorrData corr_data2;
390  corr_data2.image_id = image_id2;
391  corr_data2.point2D_idx = corr.point2D_idx2;
392  corr_data2.image = &image2;
393  corr_data2.camera = &camera2;
394  corr_data2.point2D = &point2D2;
395 
396  if (point2D1.HasPoint3D() && !point2D2.HasPoint3D()) {
397  const std::vector<CorrData> corrs_data1 = {corr_data1};
398  num_tris += Continue(re_options, corr_data2, corrs_data1);
399  } else if (!point2D1.HasPoint3D() && point2D2.HasPoint3D()) {
400  const std::vector<CorrData> corrs_data2 = {corr_data2};
401  num_tris += Continue(re_options, corr_data1, corrs_data2);
402  } else if (!point2D1.HasPoint3D() && !point2D2.HasPoint3D()) {
403  const std::vector<CorrData> corrs_data = {corr_data1,
404  corr_data2};
405  // Do not use larger triangulation threshold as this causes
406  // significant drift when creating points (options vs.
407  // re_options).
408  num_tris += Create(options, corrs_data);
409  }
410  // Else both points have a 3D point, but we do not want to
411  // merge points in retriangulation.
412  }
413  }
414 
415  return num_tris;
416 }
417 
419  modified_point3D_ids_.insert(point3D_id);
420 }
421 
422 const std::unordered_set<point3D_t>&
424  // First remove any missing 3D points from the set.
425  for (auto it = modified_point3D_ids_.begin();
426  it != modified_point3D_ids_.end();) {
427  if (reconstruction_->ExistsPoint3D(*it)) {
428  ++it;
429  } else {
430  modified_point3D_ids_.erase(it++);
431  }
432  }
433  return modified_point3D_ids_;
434 }
435 
437  modified_point3D_ids_.clear();
438 }
439 
440 void IncrementalTriangulator::ClearCaches() {
441  camera_has_bogus_params_.clear();
442  merge_trials_.clear();
443 }
444 
445 size_t IncrementalTriangulator::Find(const Options& options,
446  const image_t image_id,
447  const point2D_t point2D_idx,
448  const size_t transitivity,
449  std::vector<CorrData>* corrs_data) {
450  const std::vector<CorrespondenceGraph::Correspondence>& corrs =
451  correspondence_graph_->FindTransitiveCorrespondences(
452  image_id, point2D_idx, transitivity);
453 
454  corrs_data->clear();
455  corrs_data->reserve(corrs.size());
456 
457  size_t num_triangulated = 0;
458 
459  for (const CorrespondenceGraph::Correspondence corr : corrs) {
460  if (!reconstruction_->ExistsImage(corr.image_id)) {
461  std::cout << "[IncrementalTriangulator::Find] Image "
462  << corr.image_id << " does not exist" << std::endl;
463  continue;
464  }
465 
466  const Image& corr_image = reconstruction_->Image(corr.image_id);
467  if (!corr_image.IsRegistered()) {
468  continue;
469  }
470 
471  const Camera& corr_camera =
472  reconstruction_->Camera(corr_image.CameraId());
473  if (HasCameraBogusParams(options, corr_camera)) {
474  continue;
475  }
476 
477  CorrData corr_data;
478  corr_data.image_id = corr.image_id;
479  corr_data.point2D_idx = corr.point2D_idx;
480  corr_data.image = &corr_image;
481  corr_data.camera = &corr_camera;
482  corr_data.point2D = &corr_image.Point2D(corr.point2D_idx);
483 
484  corrs_data->push_back(corr_data);
485 
486  if (corr_data.point2D->HasPoint3D()) {
487  num_triangulated += 1;
488  }
489  }
490 
491  return num_triangulated;
492 }
493 
494 size_t IncrementalTriangulator::Create(
495  const Options& options, const std::vector<CorrData>& corrs_data) {
496  // Extract correspondences without an existing triangulated observation.
497  std::vector<CorrData> create_corrs_data;
498  create_corrs_data.reserve(corrs_data.size());
499  for (const CorrData& corr_data : corrs_data) {
500  if (!corr_data.point2D->HasPoint3D()) {
501  create_corrs_data.push_back(corr_data);
502  }
503  }
504 
505  if (create_corrs_data.size() < 2) {
506  // Need at least two observations for triangulation.
507  return 0;
508  } else if (options.ignore_two_view_tracks &&
509  create_corrs_data.size() == 2) {
510  const CorrData& corr_data1 = create_corrs_data[0];
511  if (correspondence_graph_->IsTwoViewObservation(
512  corr_data1.image_id, corr_data1.point2D_idx)) {
513  return 0;
514  }
515  }
516 
517  // Setup data for triangulation estimation.
518  std::vector<TriangulationEstimator::PointData> point_data;
519  point_data.resize(create_corrs_data.size());
520  std::vector<TriangulationEstimator::PoseData> pose_data;
521  pose_data.resize(create_corrs_data.size());
522  for (size_t i = 0; i < create_corrs_data.size(); ++i) {
523  const CorrData& corr_data = create_corrs_data[i];
524  point_data[i].point = corr_data.point2D->XY();
525  point_data[i].point_normalized =
526  corr_data.camera->ImageToWorld(point_data[i].point);
527  pose_data[i].proj_matrix = corr_data.image->ProjectionMatrix();
528  pose_data[i].proj_center = corr_data.image->ProjectionCenter();
529  pose_data[i].camera = corr_data.camera;
530  }
531 
532  // Setup estimation options.
533  EstimateTriangulationOptions tri_options;
534  tri_options.min_tri_angle = DegToRad(options.min_angle);
535  tri_options.residual_type =
537  tri_options.ransac_options.max_error =
538  DegToRad(options.create_max_angle_error);
539  tri_options.ransac_options.confidence = 0.9999;
540  tri_options.ransac_options.min_inlier_ratio = 0.02;
541  tri_options.ransac_options.max_num_trials = 10000;
542 
543  // Enforce exhaustive sampling for small track lengths.
544  const size_t kExhaustiveSamplingThreshold = 15;
545  if (point_data.size() <= kExhaustiveSamplingThreshold) {
546  tri_options.ransac_options.min_num_trials =
547  NChooseK(point_data.size(), 2);
548  }
549 
550  // Estimate triangulation.
551  Eigen::Vector3d xyz;
552  std::vector<char> inlier_mask;
553  if (!EstimateTriangulation(tri_options, point_data, pose_data, &inlier_mask,
554  &xyz)) {
555  return 0;
556  }
557 
558  // Add inliers to estimated track.
559  Track track;
560  track.Reserve(create_corrs_data.size());
561  for (size_t i = 0; i < inlier_mask.size(); ++i) {
562  if (inlier_mask[i]) {
563  const CorrData& corr_data = create_corrs_data[i];
564  track.AddElement(corr_data.image_id, corr_data.point2D_idx);
565  }
566  }
567 
568  // Add estimated point to reconstruction.
569  const point3D_t point3D_id = reconstruction_->AddPoint3D(xyz, track);
570  modified_point3D_ids_.insert(point3D_id);
571 
572  const size_t kMinRecursiveTrackLength = 3;
573  if (create_corrs_data.size() - track.Length() >= kMinRecursiveTrackLength) {
574  return track.Length() + Create(options, create_corrs_data);
575  }
576 
577  return track.Length();
578 }
579 
580 size_t IncrementalTriangulator::Continue(
581  const Options& options,
582  const CorrData& ref_corr_data,
583  const std::vector<CorrData>& corrs_data) {
584  // No need to continue, if the reference observation is triangulated.
585  if (ref_corr_data.point2D->HasPoint3D()) {
586  return 0;
587  }
588 
589  double best_angle_error = std::numeric_limits<double>::max();
590  size_t best_idx = std::numeric_limits<size_t>::max();
591 
592  for (size_t idx = 0; idx < corrs_data.size(); ++idx) {
593  const CorrData& corr_data = corrs_data[idx];
594  if (!corr_data.point2D->HasPoint3D()) {
595  continue;
596  }
597 
598  const Point3D& point3D =
599  reconstruction_->Point3D(corr_data.point2D->Point3DId());
600 
601  const double angle_error = CalculateAngularError(
602  ref_corr_data.point2D->XY(), point3D.XYZ(),
603  ref_corr_data.image->Qvec(), ref_corr_data.image->Tvec(),
604  *ref_corr_data.camera);
605  if (angle_error < best_angle_error) {
606  best_angle_error = angle_error;
607  best_idx = idx;
608  }
609  }
610 
611  const double max_angle_error = DegToRad(options.continue_max_angle_error);
612  if (best_angle_error <= max_angle_error &&
613  best_idx != std::numeric_limits<size_t>::max()) {
614  const CorrData& corr_data = corrs_data[best_idx];
615  const TrackElement track_el(ref_corr_data.image_id,
616  ref_corr_data.point2D_idx);
617  reconstruction_->AddObservation(corr_data.point2D->Point3DId(),
618  track_el);
619  modified_point3D_ids_.insert(corr_data.point2D->Point3DId());
620  return 1;
621  }
622 
623  return 0;
624 }
625 
626 size_t IncrementalTriangulator::Merge(const Options& options,
627  const point3D_t point3D_id) {
628  if (!reconstruction_->ExistsPoint3D(point3D_id)) {
629  return 0;
630  }
631 
632  const double max_squared_reproj_error =
633  options.merge_max_reproj_error * options.merge_max_reproj_error;
634 
635  const auto& point3D = reconstruction_->Point3D(point3D_id);
636 
637  for (const auto& track_el : point3D.Track().Elements()) {
638  const std::vector<CorrespondenceGraph::Correspondence>& corrs =
639  correspondence_graph_->FindCorrespondences(
640  track_el.image_id, track_el.point2D_idx);
641 
642  for (const auto corr : corrs) {
643  const auto& image = reconstruction_->Image(corr.image_id);
644  if (!image.IsRegistered()) {
645  continue;
646  }
647 
648  const Point2D& corr_point2D = image.Point2D(corr.point2D_idx);
649  if (!corr_point2D.HasPoint3D() ||
650  corr_point2D.Point3DId() == point3D_id ||
651  merge_trials_[point3D_id].count(corr_point2D.Point3DId()) > 0) {
652  continue;
653  }
654 
655  // Try to merge the two 3D points.
656 
657  const Point3D& corr_point3D =
658  reconstruction_->Point3D(corr_point2D.Point3DId());
659 
660  merge_trials_[point3D_id].insert(corr_point2D.Point3DId());
661  merge_trials_[corr_point2D.Point3DId()].insert(point3D_id);
662 
663  // Weighted average of point locations, depending on track length.
664  const Eigen::Vector3d merged_xyz =
665  (point3D.Track().Length() * point3D.XYZ() +
666  corr_point3D.Track().Length() * corr_point3D.XYZ()) /
667  (point3D.Track().Length() + corr_point3D.Track().Length());
668 
669  // Count number of inlier track elements of the merged track.
670  bool merge_success = true;
671  for (const Track* track :
672  {&point3D.Track(), &corr_point3D.Track()}) {
673  for (const auto test_track_el : track->Elements()) {
674  const Image& test_image =
675  reconstruction_->Image(test_track_el.image_id);
676  const Camera& test_camera =
677  reconstruction_->Camera(test_image.CameraId());
678  const Point2D& test_point2D =
679  test_image.Point2D(test_track_el.point2D_idx);
681  test_point2D.XY(), merged_xyz,
682  test_image.Qvec(), test_image.Tvec(),
683  test_camera) > max_squared_reproj_error) {
684  merge_success = false;
685  break;
686  }
687  }
688  if (!merge_success) {
689  break;
690  }
691  }
692 
693  // Only accept merge if all track elements are inliers.
694  if (merge_success) {
695  const size_t num_merged = point3D.Track().Length() +
696  corr_point3D.Track().Length();
697 
698  const point3D_t merged_point3D_id =
699  reconstruction_->MergePoints3D(
700  point3D_id, corr_point2D.Point3DId());
701 
702  modified_point3D_ids_.erase(point3D_id);
703  modified_point3D_ids_.erase(corr_point2D.Point3DId());
704  modified_point3D_ids_.insert(merged_point3D_id);
705 
706  // Merge merged 3D point and return, as the original points are
707  // deleted.
708  const size_t num_merged_recursive =
709  Merge(options, merged_point3D_id);
710  if (num_merged_recursive > 0) {
711  return num_merged_recursive;
712  } else {
713  return num_merged;
714  }
715  }
716  }
717  }
718 
719  return 0;
720 }
721 
722 size_t IncrementalTriangulator::Complete(const Options& options,
723  const point3D_t point3D_id) {
724  size_t num_completed = 0;
725 
726  if (!reconstruction_->ExistsPoint3D(point3D_id)) {
727  return num_completed;
728  }
729 
730  const double max_squared_reproj_error = options.complete_max_reproj_error *
731  options.complete_max_reproj_error;
732 
733  const Point3D& point3D = reconstruction_->Point3D(point3D_id);
734 
735  std::vector<TrackElement> queue = point3D.Track().Elements();
736 
737  const int max_transitivity = options.complete_max_transitivity;
738  for (int transitivity = 0; transitivity < max_transitivity;
739  ++transitivity) {
740  if (queue.empty()) {
741  break;
742  }
743 
744  const auto prev_queue = queue;
745  queue.clear();
746 
747  for (const TrackElement queue_elem : prev_queue) {
748  const std::vector<CorrespondenceGraph::Correspondence>& corrs =
749  correspondence_graph_->FindCorrespondences(
750  queue_elem.image_id, queue_elem.point2D_idx);
751 
752  for (const auto corr : corrs) {
753  const Image& image = reconstruction_->Image(corr.image_id);
754  if (!image.IsRegistered()) {
755  continue;
756  }
757 
758  const Point2D& point2D = image.Point2D(corr.point2D_idx);
759  if (point2D.HasPoint3D()) {
760  continue;
761  }
762 
763  const Camera& camera =
764  reconstruction_->Camera(image.CameraId());
765  if (HasCameraBogusParams(options, camera)) {
766  continue;
767  }
768 
770  point2D.XY(), point3D.XYZ(), image.Qvec(),
771  image.Tvec(), camera) > max_squared_reproj_error) {
772  continue;
773  }
774 
775  // Success, add observation to point track.
776  const TrackElement track_el(corr.image_id, corr.point2D_idx);
777  reconstruction_->AddObservation(point3D_id, track_el);
778  modified_point3D_ids_.insert(point3D_id);
779 
780  // Recursively complete track for this new correspondence.
781  if (transitivity < max_transitivity - 1) {
782  queue.emplace_back(corr.image_id, corr.point2D_idx);
783  }
784 
785  num_completed += 1;
786  }
787  }
788  }
789 
790  return num_completed;
791 }
792 
793 bool IncrementalTriangulator::HasCameraBogusParams(const Options& options,
794  const Camera& camera) {
795  const auto it = camera_has_bogus_params_.find(camera.CameraId());
796  if (it == camera_has_bogus_params_.end()) {
797  const bool has_bogus_params = camera.HasBogusParams(
798  options.min_focal_length_ratio, options.max_focal_length_ratio,
799  options.max_extra_param);
800  camera_has_bogus_params_.emplace(camera.CameraId(), has_bogus_params);
801  return has_bogus_params;
802  } else {
803  return it->second;
804  }
805 }
806 
807 } // namespace colmap
std::shared_ptr< core::Tensor > image
Eigen::Vector2d ImageToWorld(const Eigen::Vector2d &image_point) const
Definition: camera.cc:219
const std::vector< Correspondence > & FindCorrespondences(const image_t image_id, const point2D_t point2D_idx) const
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
bool IsTwoViewObservation(const image_t image_id, const point2D_t point2D_idx) const
static void PairIdToImagePair(const image_pair_t pair_id, image_t *image_id1, image_t *image_id2)
Definition: database.h:352
Eigen::Matrix3x4d ProjectionMatrix() const
Definition: image.cc:140
const class Point2D & Point2D(const point2D_t point2D_idx) const
Definition: image.h:349
bool IsRegistered() const
Definition: image.h:257
Eigen::Vector3d ProjectionCenter() const
Definition: image.cc:152
camera_t CameraId() const
Definition: image.h:248
size_t TriangulateImage(const Options &options, const image_t image_id)
void AddModifiedPoint3D(const point3D_t point3D_id)
const std::unordered_set< point3D_t > & GetModifiedPoints3D()
size_t CompleteTracks(const Options &options, const std::unordered_set< point3D_t > &point3D_ids)
size_t MergeAllTracks(const Options &options)
size_t MergeTracks(const Options &options, const std::unordered_set< point3D_t > &point3D_ids)
size_t CompleteImage(const Options &options, const image_t image_id)
size_t CompleteAllTracks(const Options &options)
size_t Retriangulate(const Options &options)
IncrementalTriangulator(const CorrespondenceGraph *correspondence_graph, Reconstruction *reconstruction)
point3D_t Point3DId() const
Definition: point2d.h:63
bool HasPoint3D() const
Definition: point2d.h:65
const Eigen::Vector2d & XY() const
Definition: point2d.h:53
point3D_t MergePoints3D(const point3D_t point3D_id1, const point3D_t point3D_id2)
const class Image & Image(const image_t image_id) const
std::unordered_set< point3D_t > Point3DIds() const
const class Camera & Camera(const camera_t camera_id) const
void AddObservation(const point3D_t point3D_id, const TrackElement &track_el)
const std::unordered_map< image_pair_t, ImagePairStat > & ImagePairs() const
bool ExistsImage(const image_t image_id) const
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())
bool ExistsPoint3D(const point3D_t point3D_id) const
void AddElement(const TrackElement &element)
Definition: track.h:103
void Reserve(const size_t num_elements)
Definition: track.h:120
#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
QTextStream & endl(QTextStream &stream)
Definition: QtCompat.h:718
double CalculateSquaredReprojectionError(const Eigen::Vector2d &point2D, const Eigen::Vector3d &point3D, const Eigen::Vector4d &qvec, const Eigen::Vector3d &tvec, const Camera &camera)
Definition: projection.cc:111
bool EstimateTriangulation(const EstimateTriangulationOptions &options, const std::vector< TriangulationEstimator::PointData > &point_data, const std::vector< TriangulationEstimator::PoseData > &pose_data, std::vector< char > *inlier_mask, Eigen::Vector3d *xyz)
uint64_t point3D_t
Definition: types.h:72
uint32_t point2D_t
Definition: types.h:67
uint32_t image_t
Definition: types.h:61
size_t NChooseK(const size_t n, const size_t k)
Definition: math.cc:36
float DegToRad(const float deg)
Definition: math.h:171
std::vector< FeatureMatch > FeatureMatches
Definition: types.h:80
double CalculateAngularError(const Eigen::Vector2d &point2D, const Eigen::Vector3d &point3D, const Eigen::Vector4d &qvec, const Eigen::Vector3d &tvec, const Camera &camera)
Definition: projection.cc:151
TriangulationEstimator::ResidualType residual_type
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