48 : correspondence_graph_(nullptr), num_added_points3D_(0) {}
51 : correspondence_graph_(other.GetCorrespondenceGraph()),
52 cameras_(other.Cameras()),
53 images_(other.Images()),
54 points3D_(other.Points3D()),
55 image_pair_stats_(other.ImagePairs()),
56 reg_image_ids_(other.RegImageIds()),
57 num_added_points3D_(other.NumAddedPoints3D()) {}
73 std::unordered_set<point3D_t> point3D_ids;
74 point3D_ids.reserve(points3D_.size());
76 for (
const auto& point3D : points3D_) {
77 point3D_ids.insert(point3D.first);
84 correspondence_graph_ =
nullptr;
88 for (
const auto& camera : database_cache.
Cameras()) {
96 images_.reserve(database_cache.
NumImages());
101 CHECK_EQ(existing_image.
Name(),
image.second.Name());
105 CHECK_EQ(
image.second.NumPoints2D(),
110 image.second.NumCorrespondences());
121 image_pair_stats_.emplace(image_pair.first, image_pair_stat);
126 CHECK_NOTNULL(correspondence_graph);
127 for (
auto&
image : images_) {
130 correspondence_graph_ = correspondence_graph;
134 for (
const auto image_id : reg_image_ids_) {
136 for (
point2D_t point2D_idx = 0; point2D_idx <
image.NumPoints2D();
138 if (
image.Point2D(point2D_idx).HasPoint3D()) {
139 const bool kIsContinuedPoint3D =
false;
140 SetObservationAsTriangulated(image_id, point2D_idx,
141 kIsContinuedPoint3D);
148 correspondence_graph_ =
nullptr;
149 image_pair_stats_.clear();
152 std::unordered_set<camera_t> keep_camera_ids;
153 for (
auto it = images_.begin(); it != images_.end();) {
154 if (it->second.IsRegistered()) {
155 keep_camera_ids.insert(it->second.CameraId());
156 it->second.TearDown();
159 it = images_.erase(it);
164 for (
auto it = cameras_.begin(); it != cameras_.end();) {
165 if (keep_camera_ids.count(it->first) == 0) {
166 it = cameras_.erase(it);
173 for (
auto& point3D : points3D_) {
174 point3D.second.Track().Compress();
181 cameras_.emplace(camera.
CameraId(), camera);
192 const point3D_t point3D_id = ++num_added_points3D_;
195 class Point3D& point3D = points3D_[point3D_id];
201 for (
const auto& track_el : track.
Elements()) {
203 CHECK(!
image.Point2D(track_el.point2D_idx).HasPoint3D());
204 image.SetPoint3DForPoint2D(track_el.point2D_idx, point3D_id);
205 CHECK_LE(
image.NumPoints3D(),
image.NumPoints2D());
208 const bool kIsContinuedPoint3D =
false;
210 for (
const auto& track_el : track.
Elements()) {
211 SetObservationAsTriangulated(track_el.image_id, track_el.point2D_idx,
212 kIsContinuedPoint3D);
224 CHECK_LE(
image.NumPoints3D(),
image.NumPoints2D());
229 const bool kIsContinuedPoint3D =
true;
231 kIsContinuedPoint3D);
239 const Eigen::Vector3d merged_xyz =
243 const Eigen::Vector3d merged_rgb =
257 AddPoint3D(merged_xyz, merged_track, merged_rgb.cast<uint8_t>());
259 return merged_point3D_id;
268 const bool kIsDeletedPoint3D =
true;
270 for (
const auto& track_el : track.
Elements()) {
271 ResetTriObservations(track_el.image_id, track_el.point2D_idx,
275 for (
const auto& track_el : track.
Elements()) {
277 image.ResetPoint3DForPoint2D(track_el.point2D_idx);
280 points3D_.erase(point3D_id);
289 const point3D_t point3D_id =
image.Point2D(point2D_idx).Point3DId();
299 const bool kIsDeletedPoint3D =
false;
300 ResetTriObservations(image_id, point2D_idx, kIsDeletedPoint3D);
302 image.ResetPoint3DForPoint2D(point2D_idx);
307 for (
auto&
image : images_) {
308 class Image new_image;
318 image.second = new_image;
324 if (!
image.IsRegistered()) {
325 image.SetRegistered(
true);
326 reg_image_ids_.push_back(image_id);
333 for (
point2D_t point2D_idx = 0; point2D_idx <
image.NumPoints2D();
335 if (
image.Point2D(point2D_idx).HasPoint3D()) {
340 image.SetRegistered(
false);
342 reg_image_ids_.erase(
343 std::remove(reg_image_ids_.begin(), reg_image_ids_.end(), image_id),
344 reg_image_ids_.end());
350 const bool use_images) {
353 if ((use_images && reg_image_ids_.size() < 2) ||
354 (!use_images && points3D_.size() < 2)) {
358 auto bound = ComputeBoundsAndCentroid(p0, p1, use_images);
362 const double old_extent = (std::get<1>(bound) - std::get<0>(bound)).norm();
364 if (old_extent < std::numeric_limits<double>::epsilon()) {
367 scale = extent / old_extent;
371 -scale * std::get<2>(bound));
376 const double p1)
const {
377 return std::get<2>(ComputeBoundsAndCentroid(p0, p1,
false));
381 const double p0,
const double p1)
const {
382 auto bound = ComputeBoundsAndCentroid(p0, p1,
false);
386 std::tuple<Eigen::Vector3d, Eigen::Vector3d, Eigen::Vector3d>
387 Reconstruction::ComputeBoundsAndCentroid(
const double p0,
389 const bool use_images)
const {
396 const size_t num_elements =
397 use_images ? reg_image_ids_.size() : points3D_.size();
398 if (num_elements == 0) {
399 return std::make_tuple(Eigen::Vector3d(0, 0, 0),
400 Eigen::Vector3d(0, 0, 0),
401 Eigen::Vector3d(0, 0, 0));
405 std::vector<float> coords_x;
406 std::vector<float> coords_y;
407 std::vector<float> coords_z;
409 coords_x.reserve(reg_image_ids_.size());
410 coords_y.reserve(reg_image_ids_.size());
411 coords_z.reserve(reg_image_ids_.size());
412 for (
const image_t im_id : reg_image_ids_) {
414 coords_x.push_back(
static_cast<float>(proj_center(0)));
415 coords_y.push_back(
static_cast<float>(proj_center(1)));
416 coords_z.push_back(
static_cast<float>(proj_center(2)));
419 coords_x.reserve(points3D_.size());
420 coords_y.reserve(points3D_.size());
421 coords_z.reserve(points3D_.size());
422 for (
const auto& point3D : points3D_) {
423 coords_x.push_back(
static_cast<float>(point3D.second.X()));
424 coords_y.push_back(
static_cast<float>(point3D.second.Y()));
425 coords_z.push_back(
static_cast<float>(point3D.second.Z()));
431 std::sort(coords_x.begin(), coords_x.end());
432 std::sort(coords_y.begin(), coords_y.end());
433 std::sort(coords_z.begin(), coords_z.end());
435 const size_t P0 =
static_cast<size_t>(
436 (coords_x.size() > 3) ? p0 * (coords_x.size() - 1) : 0);
437 const size_t P1 =
static_cast<size_t>((coords_x.size() > 3)
438 ? p1 * (coords_x.size() - 1)
439 : coords_x.size() - 1);
441 const Eigen::Vector3d bbox_min(coords_x[P0], coords_y[P0], coords_z[P0]);
442 const Eigen::Vector3d bbox_max(coords_x[P1], coords_y[P1], coords_z[P1]);
444 Eigen::Vector3d mean_coord(0, 0, 0);
445 for (
size_t i = P0; i <= P1; ++i) {
446 mean_coord(0) += coords_x[i];
447 mean_coord(1) += coords_y[i];
448 mean_coord(2) += coords_z[i];
450 mean_coord /= P1 - P0 + 1;
452 return std::make_tuple(bbox_min, bbox_max, mean_coord);
456 for (
auto&
image : images_) {
459 for (
auto& point3D : points3D_) {
465 const std::pair<Eigen::Vector3d, Eigen::Vector3d>& bbox)
const {
468 for (
const auto& camera_el : cameras_) {
469 reconstruction.
AddCamera(camera_el.second);
471 for (
const auto& image_el : images_) {
472 reconstruction.
AddImage(image_el.second);
473 auto&
image = reconstruction.
Image(image_el.first);
474 image.SetRegistered(
false);
476 image.ResetPoint3DForPoint2D(pid);
479 for (
const auto& point_el : points3D_) {
480 const auto& point = point_el.second;
481 if ((point.XYZ().array() >= bbox.first.array()).all() &&
482 (point.XYZ().array() <= bbox.second.array()).all()) {
483 for (
const auto& track_el : point.Track().Elements()) {
486 reconstruction.
AddPoint3D(point.XYZ(), point.Track(),
490 return reconstruction;
494 const double max_reproj_error) {
495 const double kMinInlierObservations = 0.3;
499 kMinInlierObservations,
500 max_reproj_error, &alignment)) {
508 std::unordered_set<image_t> common_image_ids;
509 common_image_ids.reserve(reconstruction.
NumRegImages());
510 std::unordered_set<image_t> missing_image_ids;
511 missing_image_ids.reserve(reconstruction.
NumRegImages());
513 for (
const auto& image_id : reconstruction.
RegImageIds()) {
515 common_image_ids.insert(image_id);
517 missing_image_ids.insert(image_id);
523 for (
const auto image_id : missing_image_ids) {
524 auto reg_image = reconstruction.
Image(image_id);
543 for (
const auto& point3D : reconstruction.
Points3D()) {
546 std::unordered_set<point3D_t> old_point3D_ids;
547 for (
const auto& track_el : point3D.second.Track().Elements()) {
548 if (common_image_ids.count(track_el.image_id) > 0) {
549 const auto& point2D =
551 if (point2D.HasPoint3D()) {
553 old_point3D_ids.insert(point2D.Point3DId());
557 }
else if (missing_image_ids.count(track_el.image_id) > 0) {
558 Image(track_el.image_id)
564 const bool create_new_point = new_track.
Length() >= 2;
565 const bool merge_new_and_old_point =
567 old_point3D_ids.size() == 1;
568 if (create_new_point || merge_new_and_old_point) {
569 Eigen::Vector3d xyz = point3D.second.XYZ();
571 const auto point3D_id =
572 AddPoint3D(xyz, new_track, point3D.second.Color());
573 if (old_point3D_ids.size() == 1) {
579 FilterPoints3DWithLargeReprojectionError(max_reproj_error,
Point3DIds());
585 const std::string&
name)
const {
586 for (
const auto&
image : images_) {
588 return &
image.second;
596 std::vector<image_t> common_reg_image_ids;
597 for (
const auto image_id : reg_image_ids_) {
600 CHECK_EQ(
Image(image_id).Name(),
602 common_reg_image_ids.push_back(image_id);
605 return common_reg_image_ids;
609 std::unordered_map<image_t, image_t> old_to_new_image_ids;
610 old_to_new_image_ids.reserve(
NumImages());
612 std::unordered_map<image_t, class Image> new_images;
615 for (
auto&
image : images_) {
617 LOG(FATAL) <<
"Image with name " <<
image.second.Name()
618 <<
" does not exist in database";
621 const auto database_image =
623 old_to_new_image_ids.emplace(
image.second.ImageId(),
624 database_image.ImageId());
625 image.second.SetImageId(database_image.ImageId());
626 new_images.emplace(database_image.ImageId(),
image.second);
629 images_ = std::move(new_images);
631 for (
auto& image_id : reg_image_ids_) {
632 image_id = old_to_new_image_ids.at(image_id);
635 for (
auto& point3D : points3D_) {
636 for (
auto& track_el : point3D.second.Track().Elements()) {
637 track_el.image_id = old_to_new_image_ids.at(track_el.image_id);
643 const double max_reproj_error,
644 const double min_tri_angle,
645 const std::unordered_set<point3D_t>& point3D_ids) {
646 size_t num_filtered = 0;
647 num_filtered += FilterPoints3DWithLargeReprojectionError(max_reproj_error,
649 num_filtered += FilterPoints3DWithSmallTriangulationAngle(min_tri_angle,
655 const double max_reproj_error,
656 const double min_tri_angle,
657 const std::unordered_set<image_t>& image_ids) {
658 std::unordered_set<point3D_t> point3D_ids;
659 for (
const image_t image_id : image_ids) {
662 if (point2D.HasPoint3D()) {
663 point3D_ids.insert(point2D.Point3DId());
667 return FilterPoints3D(max_reproj_error, min_tri_angle, point3D_ids);
671 const double min_tri_angle) {
675 const std::unordered_set<point3D_t>& point3D_ids =
Point3DIds();
676 size_t num_filtered = 0;
677 num_filtered += FilterPoints3DWithLargeReprojectionError(max_reproj_error,
679 num_filtered += FilterPoints3DWithSmallTriangulationAngle(min_tri_angle,
685 size_t num_filtered = 0;
686 for (
const auto image_id : reg_image_ids_) {
689 for (
point2D_t point2D_idx = 0; point2D_idx <
image.NumPoints2D();
705 const double min_focal_length_ratio,
706 const double max_focal_length_ratio,
707 const double max_extra_param) {
708 std::vector<image_t> filtered_image_ids;
712 if (
image.NumPoints3D() == 0) {
713 filtered_image_ids.push_back(image_id);
715 max_focal_length_ratio,
717 filtered_image_ids.push_back(image_id);
723 for (
const image_t image_id : filtered_image_ids) {
727 return filtered_image_ids;
732 for (
const image_t image_id : reg_image_ids_) {
739 if (points3D_.empty()) {
747 if (reg_image_ids_.empty()) {
751 static_cast<double>(reg_image_ids_.size());
756 double error_sum = 0.0;
757 size_t num_valid_errors = 0;
758 for (
const auto& point3D : points3D_) {
759 if (point3D.second.HasError()) {
760 error_sum += point3D.second.Error();
761 num_valid_errors += 1;
765 if (num_valid_errors == 0) {
768 return error_sum / num_valid_errors;
782 LOG(FATAL) <<
"cameras, images, points3D files do not exist at "
814 std::vector<PlyPoint> ply_points;
815 ply_points.reserve(points3D_.size());
817 for (
const auto& point3D : points3D_) {
819 ply_point.
x = point3D.second.X();
820 ply_point.
y = point3D.second.Y();
821 ply_point.
z = point3D.second.Z();
822 ply_point.
r = point3D.second.Color(0);
823 ply_point.
g = point3D.second.Color(1);
824 ply_point.
b = point3D.second.Color(2);
825 ply_points.push_back(ply_point);
836 points3D_.reserve(ply_points.size());
838 for (
const auto& ply_point : ply_points) {
839 AddPoint3D(Eigen::Vector3d(ply_point.x, ply_point.y, ply_point.z),
847 points3D_.reserve(ply_points.size());
848 for (
const auto& ply_point : ply_points) {
849 AddPoint3D(Eigen::Vector3d(ply_point.x, ply_point.y, ply_point.z),
856 bool skip_distortion)
const {
857 std::ofstream file(
path, std::ios::trunc);
858 CHECK(file.is_open()) <<
path;
865 file << reg_image_ids_.size() <<
" " <<
std::endl;
867 std::unordered_map<image_t, size_t> image_id_to_idx_;
868 size_t image_idx = 0;
870 for (
const auto image_id : reg_image_ids_) {
875 if (skip_distortion ||
883 std::cout <<
"WARNING: NVM only supports `SIMPLE_RADIAL` "
884 "and pinhole camera models."
889 const Eigen::Vector3d proj_center =
image.ProjectionCenter();
891 file <<
image.Name() <<
" ";
893 file <<
image.Qvec(0) <<
" ";
894 file <<
image.Qvec(1) <<
" ";
895 file <<
image.Qvec(2) <<
" ";
896 file <<
image.Qvec(3) <<
" ";
897 file << proj_center(0) <<
" ";
898 file << proj_center(1) <<
" ";
899 file << proj_center(2) <<
" ";
903 image_id_to_idx_[image_id] = image_idx;
909 for (
const auto& point3D : points3D_) {
910 file << point3D.second.XYZ()(0) <<
" ";
911 file << point3D.second.XYZ()(1) <<
" ";
912 file << point3D.second.XYZ()(2) <<
" ";
913 file << static_cast<int>(point3D.second.Color(0)) <<
" ";
914 file << static_cast<int>(point3D.second.Color(1)) <<
" ";
915 file << static_cast<int>(point3D.second.Color(2)) <<
" ";
917 std::ostringstream line;
919 std::unordered_set<image_t> image_ids;
920 for (
const auto& track_el : point3D.second.Track().Elements()) {
924 if (image_ids.count(track_el.image_id) == 0) {
926 const Point2D& point2D =
image.Point2D(track_el.point2D_idx);
927 line << image_id_to_idx_[track_el.image_id] <<
" ";
928 line << track_el.point2D_idx <<
" ";
929 line << point2D.
X() <<
" ";
930 line << point2D.
Y() <<
" ";
931 image_ids.insert(track_el.image_id);
935 std::string line_string = line.str();
936 line_string = line_string.substr(0, line_string.size() - 1);
938 file << image_ids.size() <<
" ";
946 bool skip_distortion)
const {
948 for (
const auto image_id : reg_image_ids_) {
949 std::string
name, ext;
955 std::ofstream file(
name, std::ios::trunc);
957 CHECK(file.is_open()) <<
name;
963 if (skip_distortion ||
976 <<
"WARNING: CAM only supports `SIMPLE_RADIAL`, `RADIAL`, "
977 "and pinhole camera models."
986 if (k1 != 0.0 && k2 == 0.0) {
1000 focal_length = fy / camera.
Height();
1002 focal_length = fx / camera.
Width();
1005 const Eigen::Matrix3d rot_mat =
image.RotationMatrix();
1007 <<
" " << rot_mat(0, 0) <<
" " << rot_mat(0, 1) <<
" "
1008 << rot_mat(0, 2) <<
" " << rot_mat(1, 0) <<
" " << rot_mat(1, 1)
1009 <<
" " << rot_mat(1, 2) <<
" " << rot_mat(2, 0) <<
" "
1010 << rot_mat(2, 1) <<
" " << rot_mat(2, 2) <<
std::endl;
1011 file << focal_length <<
" " << k1 <<
" " << k2 <<
" " << fy / fx <<
" "
1020 bool skip_distortion)
const {
1023 base_path = base_path.append(
"Recon/");
1025 std::string synth_path = base_path +
"synth_0.out";
1026 std::string image_list_path = base_path +
"urd-images.txt";
1027 std::string image_map_path = base_path +
"imagemap_0.txt";
1029 std::ofstream synth_file(synth_path, std::ios::trunc);
1030 CHECK(synth_file.is_open()) << synth_path;
1031 std::ofstream image_list_file(image_list_path, std::ios::trunc);
1032 CHECK(image_list_file.is_open()) << image_list_path;
1033 std::ofstream image_map_file(image_map_path, std::ios::trunc);
1034 CHECK(image_map_file.is_open()) << image_map_path;
1037 synth_file.precision(17);
1040 synth_file <<
"colmap 1.0" <<
std::endl;
1041 synth_file << reg_image_ids_.size() <<
" " << points3D_.size() <<
std::endl;
1043 std::unordered_map<image_t, size_t> image_id_to_idx_;
1044 size_t image_idx = 0;
1047 for (
const auto image_id : reg_image_ids_) {
1052 if (skip_distortion ||
1065 std::cout <<
"WARNING: Recon3D only supports `SIMPLE_RADIAL`, "
1066 "`RADIAL`, and pinhole camera models."
1071 const double scale =
1072 1.0 / (double)std::max(camera.
Width(), camera.
Height());
1073 synth_file << scale * camera.
MeanFocalLength() <<
" " << k1 <<
" " << k2
1078 synth_file <<
image.Tvec(0) <<
" " <<
image.Tvec(1) <<
" "
1081 image_id_to_idx_[image_id] = image_idx;
1085 image_map_file << image_idx <<
std::endl;
1089 image_list_file.close();
1090 image_map_file.close();
1093 for (
const auto& point3D : points3D_) {
1094 auto& p = point3D.second;
1095 synth_file << p.XYZ()(0) <<
" " << p.XYZ()(1) <<
" " << p.XYZ()(2)
1097 synth_file << (int)p.Color(0) <<
" " << (int)p.Color(1) <<
" "
1100 std::ostringstream line;
1102 std::unordered_set<image_t> image_ids;
1103 for (
const auto& track_el : p.Track().Elements()) {
1107 if (image_ids.count(track_el.image_id) == 0) {
1110 const Point2D& point2D =
image.Point2D(track_el.point2D_idx);
1112 const double scale =
1113 1.0 / (double)std::max(camera.
Width(), camera.
Height());
1115 line << image_id_to_idx_[track_el.image_id] <<
" ";
1116 line << track_el.point2D_idx <<
" ";
1122 image_ids.insert(track_el.image_id);
1126 std::string line_string = line.str();
1127 line_string = line_string.substr(0, line_string.size() - 1);
1129 synth_file << image_ids.size() <<
" ";
1138 const std::string& list_path,
1139 bool skip_distortion)
const {
1140 std::ofstream file(
path, std::ios::trunc);
1141 CHECK(file.is_open()) <<
path;
1143 std::ofstream list_file(list_path, std::ios::trunc);
1144 CHECK(list_file.is_open()) << list_path;
1149 file <<
"# Bundle file v0.3" <<
std::endl;
1151 file << reg_image_ids_.size() <<
" " << points3D_.size() <<
std::endl;
1153 std::unordered_map<image_t, size_t> image_id_to_idx_;
1154 size_t image_idx = 0;
1156 for (
const image_t image_id : reg_image_ids_) {
1161 if (skip_distortion ||
1173 std::cout <<
"WARNING: Bundler only supports `SIMPLE_RADIAL`, "
1174 "`RADIAL`, and pinhole camera models."
1181 const Eigen::Matrix3d R =
image.RotationMatrix();
1182 file << R(0, 0) <<
" " << R(0, 1) <<
" " << R(0, 2) <<
std::endl;
1183 file << -R(1, 0) <<
" " << -R(1, 1) <<
" " << -R(1, 2) <<
std::endl;
1184 file << -R(2, 0) <<
" " << -R(2, 1) <<
" " << -R(2, 2) <<
std::endl;
1186 file <<
image.Tvec(0) <<
" ";
1187 file << -
image.Tvec(1) <<
" ";
1192 image_id_to_idx_[image_id] = image_idx;
1196 for (
const auto& point3D : points3D_) {
1197 file << point3D.second.XYZ()(0) <<
" ";
1198 file << point3D.second.XYZ()(1) <<
" ";
1199 file << point3D.second.XYZ()(2) <<
std::endl;
1201 file << static_cast<int>(point3D.second.Color(0)) <<
" ";
1202 file << static_cast<int>(point3D.second.Color(1)) <<
" ";
1203 file << static_cast<int>(point3D.second.Color(2)) <<
std::endl;
1205 std::ostringstream line;
1207 line << point3D.second.Track().Length() <<
" ";
1209 for (
const auto& track_el : point3D.second.Track().Elements()) {
1219 const Point2D& point2D =
image.Point2D(track_el.point2D_idx);
1221 line << image_id_to_idx_.at(track_el.image_id) <<
" ";
1222 line << track_el.point2D_idx <<
" ";
1227 std::string line_string = line.str();
1228 line_string = line_string.substr(0, line_string.size() - 1);
1239 const bool kWriteNormal =
false;
1240 const bool kWriteRGB =
true;
1245 const std::string& points3D_path,
1246 const double image_scale,
1247 const Eigen::Vector3d& image_rgb)
const {
1248 std::ofstream images_file(images_path, std::ios::trunc);
1249 CHECK(images_file.is_open()) << images_path;
1251 const double six = image_scale * 0.15;
1252 const double siy = image_scale * 0.1;
1254 std::vector<Eigen::Vector3d>
points;
1255 points.emplace_back(-six, -siy, six * 1.0 * 2.0);
1256 points.emplace_back(+six, -siy, six * 1.0 * 2.0);
1257 points.emplace_back(+six, +siy, six * 1.0 * 2.0);
1258 points.emplace_back(-six, +siy, six * 1.0 * 2.0);
1259 points.emplace_back(0, 0, 0);
1260 points.emplace_back(-six / 3.0, -siy / 3.0, six * 1.0 * 2.0);
1261 points.emplace_back(+six / 3.0, -siy / 3.0, six * 1.0 * 2.0);
1262 points.emplace_back(+six / 3.0, +siy / 3.0, six * 1.0 * 2.0);
1263 points.emplace_back(-six / 3.0, +siy / 3.0, six * 1.0 * 2.0);
1265 for (
const auto&
image : images_) {
1266 if (!
image.second.IsRegistered()) {
1270 images_file <<
"Shape{\n";
1271 images_file <<
" appearance Appearance {\n";
1272 images_file <<
" material DEF Default-ffRffGffB Material {\n";
1273 images_file <<
" ambientIntensity 0\n";
1274 images_file <<
" diffuseColor "
1275 <<
" " << image_rgb(0) <<
" " << image_rgb(1) <<
" "
1276 << image_rgb(2) <<
"\n";
1277 images_file <<
" emissiveColor 0.1 0.1 0.1 } }\n";
1278 images_file <<
" geometry IndexedFaceSet {\n";
1279 images_file <<
" solid FALSE \n";
1280 images_file <<
" colorPerVertex TRUE \n";
1281 images_file <<
" ccw TRUE \n";
1283 images_file <<
" coord Coordinate {\n";
1284 images_file <<
" point [\n";
1286 Eigen::Transform<double, 3, Eigen::Affine> transform;
1287 transform.matrix().topLeftCorner<3, 4>() =
1288 image.second.InverseProjectionMatrix();
1291 for (
size_t i = 0; i <
points.size(); i++) {
1292 const Eigen::Vector3d point = transform *
points[i];
1293 images_file << point(0) <<
" " << point(1) <<
" " << point(2)
1297 images_file <<
" ] }\n";
1299 images_file <<
"color Color {color [\n";
1300 for (
size_t p = 0; p <
points.size(); p++) {
1301 images_file <<
" " << image_rgb(0) <<
" " << image_rgb(1) <<
" "
1302 << image_rgb(2) <<
"\n";
1305 images_file <<
"\n] }\n";
1307 images_file <<
"coordIndex [\n";
1308 images_file <<
" 0, 1, 2, 3, -1\n";
1309 images_file <<
" 5, 6, 4, -1\n";
1310 images_file <<
" 6, 7, 4, -1\n";
1311 images_file <<
" 7, 8, 4, -1\n";
1312 images_file <<
" 8, 5, 4, -1\n";
1313 images_file <<
" \n] \n";
1315 images_file <<
" texCoord TextureCoordinate { point [\n";
1316 images_file <<
" 1 1,\n";
1317 images_file <<
" 0 1,\n";
1318 images_file <<
" 0 0,\n";
1319 images_file <<
" 1 0,\n";
1320 images_file <<
" 0 0,\n";
1321 images_file <<
" 0 0,\n";
1322 images_file <<
" 0 0,\n";
1323 images_file <<
" 0 0,\n";
1324 images_file <<
" 0 0,\n";
1326 images_file <<
" ] }\n";
1327 images_file <<
"} }\n";
1332 std::ofstream points3D_file(points3D_path, std::ios::trunc);
1333 CHECK(points3D_file.is_open()) << points3D_path;
1335 points3D_file <<
"#VRML V2.0 utf8\n";
1336 points3D_file <<
"Background { skyColor [1.0 1.0 1.0] } \n";
1337 points3D_file <<
"Shape{ appearance Appearance {\n";
1338 points3D_file <<
" material Material {emissiveColor 1 1 1} }\n";
1339 points3D_file <<
" geometry PointSet {\n";
1340 points3D_file <<
" coord Coordinate {\n";
1341 points3D_file <<
" point [\n";
1343 for (
const auto& point3D : points3D_) {
1344 points3D_file << point3D.second.XYZ()(0) <<
", ";
1345 points3D_file << point3D.second.XYZ()(1) <<
", ";
1346 points3D_file << point3D.second.XYZ()(2) <<
std::endl;
1349 points3D_file <<
" ] }\n";
1350 points3D_file <<
" color Color { color [\n";
1352 for (
const auto& point3D : points3D_) {
1353 points3D_file << point3D.second.Color(0) / 255.0 <<
", ";
1354 points3D_file << point3D.second.Color(1) / 255.0 <<
", ";
1355 points3D_file << point3D.second.Color(2) / 255.0 <<
std::endl;
1358 points3D_file <<
" ] } } }\n";
1362 const std::string&
path) {
1372 if (point2D.HasPoint3D()) {
1374 if (point3D.
Color() == kBlackColor) {
1379 point2D.Y() - 0.5, &
color)) {
1392 std::unordered_map<point3D_t, Eigen::Vector3d> color_sums;
1393 std::unordered_map<point3D_t, size_t> color_counts;
1395 for (
size_t i = 0; i < reg_image_ids_.size(); ++i) {
1400 if (!bitmap.
Read(image_path)) {
1401 std::cout <<
StringPrintf(
"Could not read image %s at path %s.",
1402 image.Name().c_str(), image_path.c_str())
1408 if (point2D.HasPoint3D()) {
1413 point2D.Y() - 0.5, &
color)) {
1414 if (color_sums.count(point2D.Point3DId())) {
1415 Eigen::Vector3d& color_sum =
1416 color_sums[point2D.Point3DId()];
1417 color_sum(0) +=
color.r;
1418 color_sum(1) +=
color.g;
1419 color_sum(2) +=
color.b;
1420 color_counts[point2D.Point3DId()] += 1;
1423 point2D.Point3DId(),
1425 color_counts.emplace(point2D.Point3DId(), 1);
1433 for (
auto& point3D : points3D_) {
1434 if (color_sums.count(point3D.first)) {
1435 Eigen::Vector3d
color =
1436 color_sums[point3D.first] / color_counts[point3D.first];
1440 point3D.second.SetColor(
color.cast<uint8_t>());
1442 point3D.second.SetColor(kBlackColor);
1448 std::unordered_set<std::string> image_dirs;
1449 for (
const auto&
image : images_) {
1450 const std::vector<std::string> name_split =
1452 if (name_split.size() > 1) {
1453 std::string dir =
path;
1454 for (
size_t i = 0; i < name_split.size() - 1; ++i) {
1456 image_dirs.insert(dir);
1460 for (
const auto& dir : image_dirs) {
1465 size_t Reconstruction::FilterPoints3DWithSmallTriangulationAngle(
1466 const double min_tri_angle,
1467 const std::unordered_set<point3D_t>& point3D_ids) {
1469 size_t num_filtered = 0;
1472 const double min_tri_angle_rad =
DegToRad(min_tri_angle);
1475 std::unordered_map<image_t, Eigen::Vector3d> proj_centers;
1477 for (
const auto point3D_id : point3D_ids) {
1487 bool keep_point =
false;
1488 for (
size_t i1 = 0; i1 < point3D.Track().Length(); ++i1) {
1489 const image_t image_id1 = point3D.Track().Element(i1).image_id;
1491 Eigen::Vector3d proj_center1;
1492 if (proj_centers.count(image_id1) == 0) {
1493 const class Image& image1 =
Image(image_id1);
1494 proj_center1 = image1.ProjectionCenter();
1495 proj_centers.emplace(image_id1, proj_center1);
1497 proj_center1 = proj_centers.at(image_id1);
1500 for (
size_t i2 = 0; i2 < i1; ++i2) {
1501 const image_t image_id2 = point3D.Track().Element(i2).image_id;
1502 const Eigen::Vector3d proj_center2 = proj_centers.at(image_id2);
1505 proj_center1, proj_center2, point3D.XYZ());
1507 if (tri_angle >= min_tri_angle_rad) {
1524 return num_filtered;
1527 size_t Reconstruction::FilterPoints3DWithLargeReprojectionError(
1528 const double max_reproj_error,
1529 const std::unordered_set<point3D_t>& point3D_ids) {
1530 const double max_squared_reproj_error = max_reproj_error * max_reproj_error;
1533 size_t num_filtered = 0;
1535 for (
const auto point3D_id : point3D_ids) {
1542 if (point3D.Track().Length() < 2) {
1544 num_filtered += point3D.Track().Length();
1548 double reproj_error_sum = 0.0;
1550 std::vector<TrackElement> track_els_to_delete;
1552 for (
const auto& track_el : point3D.Track().Elements()) {
1555 const Point2D& point2D =
image.Point2D(track_el.point2D_idx);
1556 const double squared_reproj_error =
1558 point2D.XY(), point3D.XYZ(),
image.Qvec(),
1559 image.Tvec(), camera);
1560 if (squared_reproj_error > max_squared_reproj_error) {
1561 track_els_to_delete.push_back(track_el);
1563 reproj_error_sum += std::sqrt(squared_reproj_error);
1567 if (track_els_to_delete.size() >= point3D.Track().Length() - 1) {
1568 num_filtered += point3D.Track().Length();
1571 num_filtered += track_els_to_delete.size();
1572 for (
const auto& track_el : track_els_to_delete) {
1575 point3D.SetError(reproj_error_sum / point3D.Track().Length());
1579 return num_filtered;
1582 void Reconstruction::ReadCamerasText(
const std::string&
path) {
1585 std::ifstream file(
path);
1586 CHECK(file.is_open()) <<
path;
1591 while (std::getline(file, line)) {
1594 if (line.empty() || line[0] ==
'#') {
1598 std::stringstream line_stream(line);
1603 std::getline(line_stream, item,
' ');
1604 camera.SetCameraId(std::stoul(item));
1607 std::getline(line_stream, item,
' ');
1608 camera.SetModelIdFromName(item);
1611 std::getline(line_stream, item,
' ');
1612 camera.SetWidth(std::stoll(item));
1615 std::getline(line_stream, item,
' ');
1616 camera.SetHeight(std::stoll(item));
1619 camera.Params().clear();
1620 while (!line_stream.eof()) {
1621 std::getline(line_stream, item,
' ');
1622 camera.Params().push_back(std::stold(item));
1625 CHECK(camera.VerifyParams());
1627 cameras_.emplace(camera.CameraId(), std::move(camera));
1631 void Reconstruction::ReadImagesText(
const std::string&
path) {
1634 std::ifstream file(
path);
1635 CHECK(file.is_open()) <<
path;
1640 while (std::getline(file, line)) {
1643 if (line.empty() || line[0] ==
'#') {
1647 std::stringstream line_stream1(line);
1650 std::getline(line_stream1, item,
' ');
1651 const image_t image_id = std::stoul(item);
1654 image.SetImageId(image_id);
1656 image.SetRegistered(
true);
1657 reg_image_ids_.push_back(image_id);
1660 std::getline(line_stream1, item,
' ');
1661 image.Qvec(0) = std::stold(item);
1663 std::getline(line_stream1, item,
' ');
1664 image.Qvec(1) = std::stold(item);
1666 std::getline(line_stream1, item,
' ');
1667 image.Qvec(2) = std::stold(item);
1669 std::getline(line_stream1, item,
' ');
1670 image.Qvec(3) = std::stold(item);
1672 image.NormalizeQvec();
1675 std::getline(line_stream1, item,
' ');
1676 image.Tvec(0) = std::stold(item);
1678 std::getline(line_stream1, item,
' ');
1679 image.Tvec(1) = std::stold(item);
1681 std::getline(line_stream1, item,
' ');
1682 image.Tvec(2) = std::stold(item);
1685 std::getline(line_stream1, item,
' ');
1686 image.SetCameraId(std::stoul(item));
1689 std::getline(line_stream1, item,
' ');
1690 image.SetName(item);
1693 if (!std::getline(file, line)) {
1698 std::stringstream line_stream2(line);
1700 std::vector<Eigen::Vector2d> points2D;
1701 std::vector<point3D_t> point3D_ids;
1703 if (!line.empty()) {
1704 while (!line_stream2.eof()) {
1705 Eigen::Vector2d point;
1707 std::getline(line_stream2, item,
' ');
1708 point.x() = std::stold(item);
1710 std::getline(line_stream2, item,
' ');
1711 point.y() = std::stold(item);
1713 points2D.push_back(point);
1715 std::getline(line_stream2, item,
' ');
1719 point3D_ids.push_back(std::stoll(item));
1725 image.SetPoints2D(points2D);
1727 for (
point2D_t point2D_idx = 0; point2D_idx <
image.NumPoints2D();
1730 image.SetPoint3DForPoint2D(point2D_idx,
1731 point3D_ids[point2D_idx]);
1735 images_.emplace(
image.ImageId(), std::move(
image));
1739 void Reconstruction::ReadPoints3DText(
const std::string&
path) {
1742 std::ifstream file(
path);
1743 CHECK(file.is_open()) <<
path;
1748 while (std::getline(file, line)) {
1751 if (line.empty() || line[0] ==
'#') {
1755 std::stringstream line_stream(line);
1758 std::getline(line_stream, item,
' ');
1759 const point3D_t point3D_id = std::stoll(item);
1763 num_added_points3D_ = std::max(num_added_points3D_, point3D_id);
1768 std::getline(line_stream, item,
' ');
1769 point3D.XYZ(0) = std::stold(item);
1771 std::getline(line_stream, item,
' ');
1772 point3D.XYZ(1) = std::stold(item);
1774 std::getline(line_stream, item,
' ');
1775 point3D.XYZ(2) = std::stold(item);
1778 std::getline(line_stream, item,
' ');
1779 point3D.Color(0) =
static_cast<uint8_t
>(std::stoi(item));
1781 std::getline(line_stream, item,
' ');
1782 point3D.Color(1) =
static_cast<uint8_t
>(std::stoi(item));
1784 std::getline(line_stream, item,
' ');
1785 point3D.Color(2) =
static_cast<uint8_t
>(std::stoi(item));
1788 std::getline(line_stream, item,
' ');
1789 point3D.SetError(std::stold(item));
1792 while (!line_stream.eof()) {
1793 TrackElement track_el;
1795 std::getline(line_stream, item,
' ');
1800 track_el.image_id = std::stoul(item);
1802 std::getline(line_stream, item,
' ');
1803 track_el.point2D_idx = std::stoul(item);
1805 point3D.Track().AddElement(track_el);
1808 point3D.Track().Compress();
1810 points3D_.emplace(point3D_id, std::move(point3D));
1814 void Reconstruction::ReadCamerasBinary(
const std::string&
path) {
1815 std::ifstream file(
path, std::ios::binary);
1816 CHECK(file.is_open()) <<
path;
1818 const size_t num_cameras = ReadBinaryLittleEndian<uint64_t>(&file);
1819 for (
size_t i = 0; i < num_cameras; ++i) {
1821 camera.
SetCameraId(ReadBinaryLittleEndian<camera_t>(&file));
1822 camera.SetModelId(ReadBinaryLittleEndian<int>(&file));
1823 camera.SetWidth(ReadBinaryLittleEndian<uint64_t>(&file));
1824 camera.SetHeight(ReadBinaryLittleEndian<uint64_t>(&file));
1825 ReadBinaryLittleEndian<double>(&file, &camera.Params());
1826 CHECK(camera.VerifyParams());
1827 cameras_.emplace(camera.CameraId(), std::move(camera));
1831 void Reconstruction::ReadImagesBinary(
const std::string&
path) {
1832 std::ifstream file(
path, std::ios::binary);
1833 CHECK(file.is_open()) <<
path;
1835 const size_t num_reg_images = ReadBinaryLittleEndian<uint64_t>(&file);
1836 for (
size_t i = 0; i < num_reg_images; ++i) {
1839 image.SetImageId(ReadBinaryLittleEndian<image_t>(&file));
1841 image.Qvec(0) = ReadBinaryLittleEndian<double>(&file);
1842 image.Qvec(1) = ReadBinaryLittleEndian<double>(&file);
1843 image.Qvec(2) = ReadBinaryLittleEndian<double>(&file);
1844 image.Qvec(3) = ReadBinaryLittleEndian<double>(&file);
1845 image.NormalizeQvec();
1847 image.Tvec(0) = ReadBinaryLittleEndian<double>(&file);
1848 image.Tvec(1) = ReadBinaryLittleEndian<double>(&file);
1849 image.Tvec(2) = ReadBinaryLittleEndian<double>(&file);
1851 image.SetCameraId(ReadBinaryLittleEndian<camera_t>(&file));
1855 file.read(&name_char, 1);
1856 if (name_char !=
'\0') {
1857 image.Name() += name_char;
1859 }
while (name_char !=
'\0');
1861 const size_t num_points2D = ReadBinaryLittleEndian<uint64_t>(&file);
1863 std::vector<Eigen::Vector2d> points2D;
1864 points2D.reserve(num_points2D);
1865 std::vector<point3D_t> point3D_ids;
1866 point3D_ids.reserve(num_points2D);
1867 for (
size_t j = 0; j < num_points2D; ++j) {
1868 const double x = ReadBinaryLittleEndian<double>(&file);
1869 const double y = ReadBinaryLittleEndian<double>(&file);
1870 points2D.emplace_back(
x,
y);
1871 point3D_ids.push_back(ReadBinaryLittleEndian<point3D_t>(&file));
1875 image.SetPoints2D(points2D);
1877 for (
point2D_t point2D_idx = 0; point2D_idx <
image.NumPoints2D();
1880 image.SetPoint3DForPoint2D(point2D_idx,
1881 point3D_ids[point2D_idx]);
1885 image.SetRegistered(
true);
1886 reg_image_ids_.push_back(
image.ImageId());
1888 images_.emplace(
image.ImageId(), std::move(
image));
1892 void Reconstruction::ReadPoints3DBinary(
const std::string&
path) {
1893 std::ifstream file(
path, std::ios::binary);
1894 CHECK(file.is_open()) <<
path;
1896 const size_t num_points3D = ReadBinaryLittleEndian<uint64_t>(&file);
1897 for (
size_t i = 0; i < num_points3D; ++i) {
1900 const point3D_t point3D_id = ReadBinaryLittleEndian<point3D_t>(&file);
1901 num_added_points3D_ = std::max(num_added_points3D_, point3D_id);
1903 point3D.XYZ()(0) = ReadBinaryLittleEndian<double>(&file);
1904 point3D.XYZ()(1) = ReadBinaryLittleEndian<double>(&file);
1905 point3D.XYZ()(2) = ReadBinaryLittleEndian<double>(&file);
1906 point3D.Color(0) = ReadBinaryLittleEndian<uint8_t>(&file);
1907 point3D.Color(1) = ReadBinaryLittleEndian<uint8_t>(&file);
1908 point3D.Color(2) = ReadBinaryLittleEndian<uint8_t>(&file);
1909 point3D.SetError(ReadBinaryLittleEndian<double>(&file));
1911 const size_t track_length = ReadBinaryLittleEndian<uint64_t>(&file);
1912 for (
size_t j = 0; j < track_length; ++j) {
1913 const image_t image_id = ReadBinaryLittleEndian<image_t>(&file);
1915 ReadBinaryLittleEndian<point2D_t>(&file);
1916 point3D.Track().AddElement(image_id, point2D_idx);
1918 point3D.Track().Compress();
1920 points3D_.emplace(point3D_id, std::move(point3D));
1924 void Reconstruction::WriteCamerasText(
const std::string&
path)
const {
1925 std::ofstream file(
path, std::ios::trunc);
1926 CHECK(file.is_open()) <<
path;
1931 file <<
"# Camera list with one line of data per camera:" <<
std::endl;
1932 file <<
"# CAMERA_ID, MODEL, WIDTH, HEIGHT, PARAMS[]" <<
std::endl;
1933 file <<
"# Number of cameras: " << cameras_.size() <<
std::endl;
1935 for (
const auto& camera : cameras_) {
1936 std::ostringstream line;
1938 line << camera.first <<
" ";
1939 line << camera.second.ModelName() <<
" ";
1940 line << camera.second.Width() <<
" ";
1941 line << camera.second.Height() <<
" ";
1943 for (
const double param : camera.second.Params()) {
1944 line << param <<
" ";
1947 std::string line_string = line.str();
1948 line_string = line_string.substr(0, line_string.size() - 1);
1954 void Reconstruction::WriteImagesText(
const std::string&
path)
const {
1955 std::ofstream file(
path, std::ios::trunc);
1956 CHECK(file.is_open()) <<
path;
1961 file <<
"# Image list with two lines of data per image:" <<
std::endl;
1962 file <<
"# IMAGE_ID, QW, QX, QY, QZ, TX, TY, TZ, CAMERA_ID, "
1965 file <<
"# POINTS2D[] as (X, Y, POINT3D_ID)" <<
std::endl;
1966 file <<
"# Number of images: " << reg_image_ids_.size()
1967 <<
", mean observations per image: "
1970 for (
const auto&
image : images_) {
1971 if (!
image.second.IsRegistered()) {
1975 std::ostringstream line;
1976 std::string line_string;
1978 line <<
image.first <<
" ";
1981 const Eigen::Vector4d normalized_qvec =
1983 line << normalized_qvec(0) <<
" ";
1984 line << normalized_qvec(1) <<
" ";
1985 line << normalized_qvec(2) <<
" ";
1986 line << normalized_qvec(3) <<
" ";
1989 line <<
image.second.Tvec(0) <<
" ";
1990 line <<
image.second.Tvec(1) <<
" ";
1991 line <<
image.second.Tvec(2) <<
" ";
1993 line <<
image.second.CameraId() <<
" ";
1995 line <<
image.second.Name();
2002 for (
const Point2D& point2D :
image.second.Points2D()) {
2003 line << point2D.X() <<
" ";
2004 line << point2D.Y() <<
" ";
2005 if (point2D.HasPoint3D()) {
2006 line << point2D.Point3DId() <<
" ";
2011 line_string = line.str();
2012 line_string = line_string.substr(0, line_string.size() - 1);
2017 void Reconstruction::WritePoints3DText(
const std::string&
path)
const {
2018 std::ofstream file(
path, std::ios::trunc);
2019 CHECK(file.is_open()) <<
path;
2024 file <<
"# 3D point list with one line of data per point:" <<
std::endl;
2025 file <<
"# POINT3D_ID, X, Y, Z, R, G, B, ERROR, "
2026 "TRACK[] as (IMAGE_ID, POINT2D_IDX)"
2028 file <<
"# Number of points: " << points3D_.size()
2031 for (
const auto& point3D : points3D_) {
2032 file << point3D.first <<
" ";
2033 file << point3D.second.XYZ()(0) <<
" ";
2034 file << point3D.second.XYZ()(1) <<
" ";
2035 file << point3D.second.XYZ()(2) <<
" ";
2036 file << static_cast<int>(point3D.second.Color(0)) <<
" ";
2037 file << static_cast<int>(point3D.second.Color(1)) <<
" ";
2038 file << static_cast<int>(point3D.second.Color(2)) <<
" ";
2039 file << point3D.second.Error() <<
" ";
2041 std::ostringstream line;
2043 for (
const auto& track_el : point3D.second.Track().Elements()) {
2044 line << track_el.image_id <<
" ";
2045 line << track_el.point2D_idx <<
" ";
2048 std::string line_string = line.str();
2049 line_string = line_string.substr(0, line_string.size() - 1);
2055 void Reconstruction::WriteCamerasBinary(
const std::string&
path)
const {
2056 std::ofstream file(
path, std::ios::trunc | std::ios::binary);
2057 CHECK(file.is_open()) <<
path;
2059 WriteBinaryLittleEndian<uint64_t>(&file, cameras_.size());
2061 for (
const auto& camera : cameras_) {
2062 WriteBinaryLittleEndian<camera_t>(&file, camera.first);
2063 WriteBinaryLittleEndian<int>(&file, camera.second.ModelId());
2064 WriteBinaryLittleEndian<uint64_t>(&file, camera.second.Width());
2065 WriteBinaryLittleEndian<uint64_t>(&file, camera.second.Height());
2066 for (
const double param : camera.second.Params()) {
2067 WriteBinaryLittleEndian<double>(&file, param);
2072 void Reconstruction::WriteImagesBinary(
const std::string&
path)
const {
2073 std::ofstream file(
path, std::ios::trunc | std::ios::binary);
2074 CHECK(file.is_open()) <<
path;
2076 WriteBinaryLittleEndian<uint64_t>(&file, reg_image_ids_.size());
2078 for (
const auto&
image : images_) {
2079 if (!
image.second.IsRegistered()) {
2083 WriteBinaryLittleEndian<image_t>(&file,
image.first);
2085 const Eigen::Vector4d normalized_qvec =
2087 WriteBinaryLittleEndian<double>(&file, normalized_qvec(0));
2088 WriteBinaryLittleEndian<double>(&file, normalized_qvec(1));
2089 WriteBinaryLittleEndian<double>(&file, normalized_qvec(2));
2090 WriteBinaryLittleEndian<double>(&file, normalized_qvec(3));
2092 WriteBinaryLittleEndian<double>(&file,
image.second.Tvec(0));
2093 WriteBinaryLittleEndian<double>(&file,
image.second.Tvec(1));
2094 WriteBinaryLittleEndian<double>(&file,
image.second.Tvec(2));
2096 WriteBinaryLittleEndian<camera_t>(&file,
image.second.CameraId());
2098 const std::string
name =
image.second.Name() +
'\0';
2099 file.write(
name.c_str(),
name.size());
2101 WriteBinaryLittleEndian<uint64_t>(&file,
image.second.NumPoints2D());
2102 for (
const Point2D& point2D :
image.second.Points2D()) {
2103 WriteBinaryLittleEndian<double>(&file, point2D.X());
2104 WriteBinaryLittleEndian<double>(&file, point2D.Y());
2105 WriteBinaryLittleEndian<point3D_t>(&file, point2D.Point3DId());
2110 void Reconstruction::WritePoints3DBinary(
const std::string&
path)
const {
2111 std::ofstream file(
path, std::ios::trunc | std::ios::binary);
2112 CHECK(file.is_open()) <<
path;
2114 WriteBinaryLittleEndian<uint64_t>(&file, points3D_.size());
2116 for (
const auto& point3D : points3D_) {
2117 WriteBinaryLittleEndian<point3D_t>(&file, point3D.first);
2118 WriteBinaryLittleEndian<double>(&file, point3D.second.XYZ()(0));
2119 WriteBinaryLittleEndian<double>(&file, point3D.second.XYZ()(1));
2120 WriteBinaryLittleEndian<double>(&file, point3D.second.XYZ()(2));
2121 WriteBinaryLittleEndian<uint8_t>(&file, point3D.second.Color(0));
2122 WriteBinaryLittleEndian<uint8_t>(&file, point3D.second.Color(1));
2123 WriteBinaryLittleEndian<uint8_t>(&file, point3D.second.Color(2));
2124 WriteBinaryLittleEndian<double>(&file, point3D.second.Error());
2126 WriteBinaryLittleEndian<uint64_t>(&file,
2127 point3D.second.Track().Length());
2128 for (
const auto& track_el : point3D.second.Track().Elements()) {
2129 WriteBinaryLittleEndian<image_t>(&file, track_el.image_id);
2130 WriteBinaryLittleEndian<point2D_t>(&file, track_el.point2D_idx);
2135 void Reconstruction::SetObservationAsTriangulated(
2138 const bool is_continued_point3D) {
2139 if (correspondence_graph_ ==
nullptr) {
2144 const Point2D& point2D =
image.Point2D(point2D_idx);
2145 const std::vector<CorrespondenceGraph::Correspondence>& corrs =
2148 CHECK(
image.IsRegistered());
2149 CHECK(point2D.HasPoint3D());
2151 for (
const auto& corr : corrs) {
2152 class Image& corr_image =
Image(corr.image_id);
2153 const Point2D& corr_point2D = corr_image.Point2D(corr.point2D_idx);
2154 corr_image.IncrementCorrespondenceHasPoint3D(corr.point2D_idx);
2158 if (point2D.Point3DId() == corr_point2D.Point3DId() &&
2159 (is_continued_point3D || image_id < corr.image_id)) {
2162 image_pair_stats_[pair_id].num_tri_corrs += 1;
2163 CHECK_LE(image_pair_stats_[pair_id].num_tri_corrs,
2164 image_pair_stats_[pair_id].num_total_corrs)
2165 <<
"The correspondence graph graph must not contain "
2172 void Reconstruction::ResetTriObservations(
const image_t image_id,
2174 const bool is_deleted_point3D) {
2175 if (correspondence_graph_ ==
nullptr) {
2180 const Point2D& point2D =
image.Point2D(point2D_idx);
2181 const std::vector<CorrespondenceGraph::Correspondence>& corrs =
2184 CHECK(
image.IsRegistered());
2185 CHECK(point2D.HasPoint3D());
2187 for (
const auto& corr : corrs) {
2188 class Image& corr_image =
Image(corr.image_id);
2189 const Point2D& corr_point2D = corr_image.Point2D(corr.point2D_idx);
2190 corr_image.DecrementCorrespondenceHasPoint3D(corr.point2D_idx);
2194 if (point2D.Point3DId() == corr_point2D.Point3DId() &&
2195 (!is_deleted_point3D || image_id < corr.image_id)) {
2198 image_pair_stats_[pair_id].num_tri_corrs -= 1;
2199 CHECK_GE(image_pair_stats_[pair_id].num_tri_corrs, 0)
2200 <<
"The scene graph graph must not contain duplicate "
std::shared_ptr< core::Tensor > image
bool Read(const std::string &path, const bool as_rgb=true)
bool InterpolateBilinear(const double x, const double y, BitmapColor< float > *color) const
bool VerifyParams() const
double FocalLengthY() const
double MeanFocalLength() const
const std::vector< double > & Params() const
camera_t CameraId() const
bool HasBogusParams(const double min_focal_length_ratio, const double max_focal_length_ratio, const double max_extra_param) const
void SetCameraId(const camera_t camera_id)
double FocalLengthX() const
const std::vector< size_t > & FocalLengthIdxs() const
double PrincipalPointX() const
double PrincipalPointY() const
const std::vector< Correspondence > & FindCorrespondences(const image_t image_id, const point2D_t point2D_idx) const
point2D_t NumCorrespondencesBetweenImages(const image_t image_id1, const image_t image_id2) const
const std::unordered_map< camera_t, class Camera > & Cameras() const
size_t NumCameras() const
const std::unordered_map< image_t, class Image > & Images() const
const class CorrespondenceGraph & CorrespondenceGraph() const
static image_pair_t ImagePairToPairId(const image_t image_id1, const image_t image_id2)
bool ExistsImageWithName(std::string name) const
Image ReadImageWithName(const std::string &name) const
point2D_t NumPoints3D() const
void SetName(const std::string &name)
void SetPoints2D(const std::vector< Eigen::Vector2d > &points)
void SetRegistered(const bool registered)
void ResetPoint3DForPoint2D(const point2D_t point2D_idx)
void SetTvecPrior(const Eigen::Vector3d &tvec)
void SetImageId(const image_t image_id)
void SetCameraId(const camera_t camera_id)
const class Point2D & Point2D(const point2D_t point2D_idx) const
void SetQvec(const Eigen::Vector4d &qvec)
const std::string & Name() const
void SetNumObservations(const point2D_t num_observations)
void SetQvecPrior(const Eigen::Vector4d &qvec)
Eigen::Vector3d ProjectionCenter() const
point2D_t NumPoints2D() const
void SetTvec(const Eigen::Vector3d &tvec)
void SetNumCorrespondences(const point2D_t num_observations)
point3D_t Point3DId() const
void SetTrack(const class Track &track)
const Eigen::Vector3d & XYZ() const
const class Track & Track() const
void SetColor(const Eigen::Vector3ub &color)
void SetXYZ(const Eigen::Vector3d &xyz)
const Eigen::Vector3ub & Color() const
void ReadBinary(const std::string &path)
void ExtractColorsForAllImages(const std::string &path)
bool ExportCam(const std::string &path, bool skip_distortion=false) const
size_t FilterAllPoints3D(const double max_reproj_error, const double min_tri_angle)
Reconstruction Crop(const std::pair< Eigen::Vector3d, Eigen::Vector3d > &bbox) const
double ComputeMeanTrackLength() const
point3D_t MergePoints3D(const point3D_t point3D_id1, const point3D_t point3D_id2)
size_t NumRegImages() const
Reconstruction & operator=(const Reconstruction &other)
void Transform(const SimilarityTransform3 &tform)
std::pair< Eigen::Vector3d, Eigen::Vector3d > ComputeBoundingBox(const double p0=0.0, const double p1=1.0) const
size_t FilterObservationsWithNegativeDepth()
void Write(const std::string &path) const
void DeleteAllPoints2DAndPoints3D()
std::vector< image_t > FindCommonRegImageIds(const Reconstruction &reconstruction) const
void Normalize(const double extent=10.0, const double p0=0.1, const double p1=0.9, const bool use_images=true)
bool IsImageRegistered(const image_t image_id) const
bool ExportNVM(const std::string &path, bool skip_distortion=false) const
void AddImage(const class Image &image)
bool ExtractColorsForImage(const image_t image_id, const std::string &path)
const std::unordered_map< image_t, class Image > & Images() const
void AddCamera(const class Camera &camera)
std::vector< image_t > FilterImages(const double min_focal_length_ratio, const double max_focal_length_ratio, const double max_extra_param)
void DeleteObservation(const image_t image_id, const point2D_t point2D_idx)
size_t ComputeNumObservations() const
Eigen::Vector3d ComputeCentroid(const double p0=0.1, const double p1=0.9) const
const class Image & Image(const image_t image_id) const
bool ExportRecon3D(const std::string &path, bool skip_distortion=false) const
void ExportPLY(const std::string &path) const
std::unordered_set< point3D_t > Point3DIds() const
const class Camera & Camera(const camera_t camera_id) const
void WriteBinary(const std::string &path) const
std::vector< PlyPoint > ConvertToPLY() const
bool Merge(const Reconstruction &reconstruction, const double max_reproj_error)
void ExportVRML(const std::string &images_path, const std::string &points3D_path, const double image_scale, const Eigen::Vector3d &image_rgb) const
bool ExportBundler(const std::string &path, const std::string &list_path, bool skip_distortion=false) const
void CreateImageDirs(const std::string &path) const
const std::unordered_map< camera_t, class Camera > & Cameras() const
void Load(const DatabaseCache &database_cache)
const std::unordered_map< point3D_t, class Point3D > & Points3D() const
bool ExistsCamera(const camera_t camera_id) const
void AddObservation(const point3D_t point3D_id, const TrackElement &track_el)
const class Image * FindImageWithName(const std::string &name) const
const std::unordered_map< image_pair_t, ImagePairStat > & ImagePairs() const
void DeRegisterImage(const image_t image_id)
void ReadText(const std::string &path)
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)
void DeletePoint3D(const point3D_t point3D_id)
double ComputeMeanReprojectionError() const
bool ExistsImage(const image_t image_id) const
double ComputeMeanObservationsPerRegImage() const
void RegisterImage(const image_t image_id)
void WriteText(const std::string &path) const
size_t FilterPoints3DInImages(const double max_reproj_error, const double min_tri_angle, const std::unordered_set< image_t > &image_ids)
void ImportPLY(const std::string &path)
void Read(const std::string &path)
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)
size_t NumAddedPoints3D() const
bool ExistsPoint3D(const point3D_t point3D_id) const
const CorrespondenceGraph * GetCorrespondenceGraph() const
void TranscribeImageIdsToDatabase(const Database &database)
void AddElement(const TrackElement &element)
void AddElements(const std::vector< TrackElement > &elements)
const std::vector< TrackElement > & Elements() const
void DeleteElement(const size_t idx)
void Reserve(const size_t num_elements)
Matrix< double, 3, 4 > Matrix3x4d
Matrix< uint8_t, 3, 1 > Vector3ub
QTextStream & endl(QTextStream &stream)
CLOUDVIEWER_HOST_DEVICE Pair< First, Second > make_pair(const First &_first, const Second &_second)
static const std::string path
bool ComputeAlignmentBetweenReconstructions(const Reconstruction &src_reconstruction, const Reconstruction &ref_reconstruction, const double min_inlier_observations, const double max_reproj_error, Eigen::Matrix3x4d *alignment)
double CalculateSquaredReprojectionError(const Eigen::Vector2d &point2D, const Eigen::Vector3d &point3D, const Eigen::Vector4d &qvec, const Eigen::Vector3d &tvec, const Camera &camera)
bool HasPointPositiveDepth(const Eigen::Matrix3x4d &proj_matrix, const Eigen::Vector3d &point3D)
Eigen::Vector4d ComposeIdentityQuaternion()
void StringTrim(std::string *str)
Eigen::Vector4d NormalizeQuaternion(const Eigen::Vector4d &qvec)
std::string StringReplace(const std::string &str, const std::string &old_str, const std::string &new_str)
void WriteBinaryPlyPoints(const std::string &path, const std::vector< PlyPoint > &points, const bool write_normal, const bool write_rgb)
void SplitFileExtension(const std::string &path, std::string *root, std::string *ext)
bool ExistsFile(const std::string &path)
void CreateDirIfNotExists(const std::string &path)
std::string JoinPaths(T const &... paths)
std::string EnsureTrailingSlash(const std::string &str)
const point3D_t kInvalidPoint3DId
std::vector< std::string > StringSplit(const std::string &str, const std::string &delim)
std::string StringPrintf(const char *format,...)
double CalculateTriangulationAngle(const Eigen::Vector3d &proj_center1, const Eigen::Vector3d &proj_center2, const Eigen::Vector3d &point3D)
float DegToRad(const float deg)
std::vector< PlyPoint > ReadPly(const std::string &path)
Eigen::Matrix3d QuaternionToRotationMatrix(const Eigen::Vector4d &qvec)
Eigen::MatrixXd::Index Index
static const int model_id
static const int model_id
static const std::vector< size_t > extra_params_idxs
static const int model_id
static const std::vector< size_t > extra_params_idxs
static const int model_id