58 : correspondence_graph_(correspondence_graph),
59 reconstruction_(reconstruction) {}
63 CHECK(options.
Check());
70 std::cout <<
"[IncrementalTriangulator::TriangulateImage] Image "
71 << image_id <<
" does not exist" <<
std::endl;
76 if (!
image.IsRegistered()) {
81 if (HasCameraBogusParams(options, camera)) {
91 ref_corr_data.
camera = &camera;
94 std::vector<CorrData> corrs_data;
99 const size_t num_triangulated = Find(
100 options, image_id, point2D_idx,
102 if (corrs_data.empty()) {
108 ref_corr_data.
point2D = &point2D;
110 if (num_triangulated == 0) {
111 corrs_data.push_back(ref_corr_data);
112 num_tris += Create(options, corrs_data);
115 num_tris += Continue(options, ref_corr_data, corrs_data);
117 corrs_data.push_back(ref_corr_data);
118 num_tris += Create(options, corrs_data);
127 CHECK(options.
Check());
134 std::cout <<
"[IncrementalTriangulator::CompleteImage] Image "
135 << image_id <<
" does not exist" <<
std::endl;
140 if (!
image.IsRegistered()) {
145 if (HasCameraBogusParams(options, camera)) {
165 ref_corr_data.
camera = &camera;
168 std::vector<CorrData> corrs_data;
170 for (
point2D_t point2D_idx = 0; point2D_idx <
image.NumPoints2D();
175 num_tris += Complete(options, point2D.
Point3DId());
185 const size_t num_triangulated = Find(
186 options, image_id, point2D_idx,
188 if (num_triangulated || corrs_data.empty()) {
192 ref_corr_data.
point2D = &point2D;
194 corrs_data.push_back(ref_corr_data);
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 =
208 pose_data[i].camera = corr_data.
camera;
212 const size_t kExhaustiveSamplingThreshold = 15;
213 if (point_data.size() <= kExhaustiveSamplingThreshold) {
220 std::vector<char> inlier_mask;
222 &inlier_mask, &xyz)) {
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];
238 modified_point3D_ids_.insert(point3D_id);
246 const std::unordered_set<point3D_t>& point3D_ids) {
247 CHECK(options.
Check());
249 size_t num_completed = 0;
253 for (
const point3D_t point3D_id : point3D_ids) {
254 num_completed += Complete(options, point3D_id);
257 return num_completed;
261 CHECK(options.
Check());
263 size_t num_completed = 0;
268 num_completed += Complete(options, point3D_id);
271 return num_completed;
276 const std::unordered_set<point3D_t>& point3D_ids) {
277 CHECK(options.
Check());
279 size_t num_merged = 0;
283 for (
const point3D_t point3D_id : point3D_ids) {
284 num_merged += Merge(options, point3D_id);
291 CHECK(options.
Check());
293 size_t num_merged = 0;
298 num_merged += Merge(options, point3D_id);
305 CHECK(options.
Check());
314 for (
const auto& image_pair : reconstruction_->
ImagePairs()) {
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);
331 std::cout <<
"[IncrementalTriangulator::Retriangulate] Image "
332 << image_id1 <<
" or " << image_id2 <<
" does not exist"
337 const Image& image1 = reconstruction_->
Image(image_id1);
342 const Image& image2 = reconstruction_->
Image(image_id2);
349 int& num_re_trials = re_num_trials_[image_pair.first];
357 if (HasCameraBogusParams(options, camera1) ||
358 HasCameraBogusParams(options, camera2)) {
366 image_id1, image_id2);
368 for (
const auto& corr : corrs) {
385 corr_data1.
image = &image1;
386 corr_data1.
camera = &camera1;
387 corr_data1.
point2D = &point2D1;
392 corr_data2.
image = &image2;
393 corr_data2.
camera = &camera2;
394 corr_data2.
point2D = &point2D2;
397 const std::vector<CorrData> corrs_data1 = {corr_data1};
398 num_tris += Continue(re_options, corr_data2, corrs_data1);
400 const std::vector<CorrData> corrs_data2 = {corr_data2};
401 num_tris += Continue(re_options, corr_data1, corrs_data2);
403 const std::vector<CorrData> corrs_data = {corr_data1,
408 num_tris += Create(options, corrs_data);
419 modified_point3D_ids_.insert(point3D_id);
422 const std::unordered_set<point3D_t>&
425 for (
auto it = modified_point3D_ids_.begin();
426 it != modified_point3D_ids_.end();) {
430 modified_point3D_ids_.erase(it++);
433 return modified_point3D_ids_;
437 modified_point3D_ids_.clear();
440 void IncrementalTriangulator::ClearCaches() {
441 camera_has_bogus_params_.clear();
442 merge_trials_.clear();
445 size_t IncrementalTriangulator::Find(
const Options& options,
448 const size_t transitivity,
449 std::vector<CorrData>* corrs_data) {
450 const std::vector<CorrespondenceGraph::Correspondence>& corrs =
452 image_id, point2D_idx, transitivity);
455 corrs_data->reserve(corrs.size());
457 size_t num_triangulated = 0;
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;
466 const Image& corr_image = reconstruction_->
Image(corr.image_id);
467 if (!corr_image.IsRegistered()) {
471 const Camera& corr_camera =
472 reconstruction_->
Camera(corr_image.CameraId());
473 if (HasCameraBogusParams(options, corr_camera)) {
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);
484 corrs_data->push_back(corr_data);
486 if (corr_data.point2D->HasPoint3D()) {
487 num_triangulated += 1;
491 return num_triangulated;
494 size_t IncrementalTriangulator::Create(
495 const Options& options,
const std::vector<CorrData>& corrs_data) {
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);
505 if (create_corrs_data.size() < 2) {
508 }
else if (options.ignore_two_view_tracks &&
509 create_corrs_data.size() == 2) {
510 const CorrData& corr_data1 = create_corrs_data[0];
512 corr_data1.image_id, corr_data1.point2D_idx)) {
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 =
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;
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;
544 const size_t kExhaustiveSamplingThreshold = 15;
545 if (point_data.size() <= kExhaustiveSamplingThreshold) {
546 tri_options.ransac_options.min_num_trials =
552 std::vector<char> inlier_mask;
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);
570 modified_point3D_ids_.insert(point3D_id);
572 const size_t kMinRecursiveTrackLength = 3;
573 if (create_corrs_data.size() - track.Length() >= kMinRecursiveTrackLength) {
574 return track.Length() + Create(options, create_corrs_data);
577 return track.Length();
580 size_t IncrementalTriangulator::Continue(
581 const Options& options,
582 const CorrData& ref_corr_data,
583 const std::vector<CorrData>& corrs_data) {
585 if (ref_corr_data.point2D->HasPoint3D()) {
589 double best_angle_error = std::numeric_limits<double>::max();
590 size_t best_idx = std::numeric_limits<size_t>::max();
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()) {
599 reconstruction_->
Point3D(corr_data.point2D->Point3DId());
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;
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);
619 modified_point3D_ids_.insert(corr_data.point2D->Point3DId());
626 size_t IncrementalTriangulator::Merge(
const Options& options,
632 const double max_squared_reproj_error =
633 options.merge_max_reproj_error * options.merge_max_reproj_error;
635 const auto& point3D = reconstruction_->
Point3D(point3D_id);
637 for (
const auto& track_el : point3D.Track().Elements()) {
638 const std::vector<CorrespondenceGraph::Correspondence>& corrs =
640 track_el.image_id, track_el.point2D_idx);
642 for (
const auto corr : corrs) {
643 const auto&
image = reconstruction_->
Image(corr.image_id);
644 if (!
image.IsRegistered()) {
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) {
658 reconstruction_->
Point3D(corr_point2D.Point3DId());
660 merge_trials_[point3D_id].insert(corr_point2D.Point3DId());
661 merge_trials_[corr_point2D.Point3DId()].insert(point3D_id);
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());
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;
688 if (!merge_success) {
695 const size_t num_merged = point3D.Track().Length() +
696 corr_point3D.Track().Length();
700 point3D_id, corr_point2D.Point3DId());
702 modified_point3D_ids_.erase(point3D_id);
703 modified_point3D_ids_.erase(corr_point2D.Point3DId());
704 modified_point3D_ids_.insert(merged_point3D_id);
708 const size_t num_merged_recursive =
709 Merge(options, merged_point3D_id);
710 if (num_merged_recursive > 0) {
711 return num_merged_recursive;
722 size_t IncrementalTriangulator::Complete(
const Options& options,
724 size_t num_completed = 0;
727 return num_completed;
730 const double max_squared_reproj_error = options.complete_max_reproj_error *
731 options.complete_max_reproj_error;
735 std::vector<TrackElement> queue = point3D.Track().Elements();
737 const int max_transitivity = options.complete_max_transitivity;
738 for (
int transitivity = 0; transitivity < max_transitivity;
744 const auto prev_queue = queue;
747 for (
const TrackElement queue_elem : prev_queue) {
748 const std::vector<CorrespondenceGraph::Correspondence>& corrs =
750 queue_elem.image_id, queue_elem.point2D_idx);
752 for (
const auto corr : corrs) {
753 const Image&
image = reconstruction_->
Image(corr.image_id);
754 if (!
image.IsRegistered()) {
758 const Point2D& point2D =
image.Point2D(corr.point2D_idx);
759 if (point2D.HasPoint3D()) {
765 if (HasCameraBogusParams(options, camera)) {
770 point2D.XY(), point3D.XYZ(),
image.Qvec(),
771 image.Tvec(), camera) > max_squared_reproj_error) {
776 const TrackElement track_el(corr.image_id, corr.point2D_idx);
778 modified_point3D_ids_.insert(point3D_id);
781 if (transitivity < max_transitivity - 1) {
782 queue.emplace_back(corr.image_id, corr.point2D_idx);
790 return num_completed;
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;
std::shared_ptr< core::Tensor > image
Eigen::Vector2d ImageToWorld(const Eigen::Vector2d &image_point) const
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)
Eigen::Matrix3x4d ProjectionMatrix() const
const class Point2D & Point2D(const point2D_t point2D_idx) const
bool IsRegistered() const
Eigen::Vector3d ProjectionCenter() const
camera_t CameraId() const
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)
void ClearModifiedPoints3D()
size_t CompleteAllTracks(const Options &options)
size_t Retriangulate(const Options &options)
IncrementalTriangulator(const CorrespondenceGraph *correspondence_graph, Reconstruction *reconstruction)
point3D_t Point3DId() const
const Eigen::Vector2d & XY() const
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)
void Reserve(const size_t num_elements)
#define CHECK_OPTION_LE(val1, val2)
#define CHECK_OPTION_GT(val1, val2)
#define CHECK_OPTION_GE(val1, val2)
QTextStream & endl(QTextStream &stream)
double CalculateSquaredReprojectionError(const Eigen::Vector2d &point2D, const Eigen::Vector3d &point3D, const Eigen::Vector4d &qvec, const Eigen::Vector3d &tvec, const Camera &camera)
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)
size_t NChooseK(const size_t n, const size_t k)
float DegToRad(const float deg)
std::vector< FeatureMatch > FeatureMatches
double CalculateAngularError(const Eigen::Vector2d &point2D, const Eigen::Vector3d &point3D, const Eigen::Vector4d &qvec, const Eigen::Vector3d &tvec, const Camera &camera)
RANSACOptions ransac_options
TriangulationEstimator::ResidualType residual_type
bool ignore_two_view_tracks
int complete_max_transitivity
double complete_max_reproj_error
double merge_max_reproj_error
double re_max_angle_error
double create_max_angle_error
double continue_max_angle_error