45 std::vector<std::pair<Eigen::Vector3d, Eigen::Vector3d>>
46 ComputeEqualPartsBounds(
const Reconstruction& reconstruction,
48 std::vector<std::pair<Eigen::Vector3d, Eigen::Vector3d>> bounds;
49 const auto bbox = reconstruction.ComputeBoundingBox();
50 const Eigen::Vector3d extent = bbox.second - bbox.first;
51 const Eigen::Vector3d
offset(extent(0) / split(0), extent(1) / split(1),
52 extent(2) / split(2));
54 for (
int k = 0; k < split(2); ++k) {
55 for (
int j = 0; j < split(1); ++j) {
56 for (
int i = 0; i < split(0); ++i) {
57 Eigen::Vector3d min_bound(bbox.first(0) + i *
offset(0),
58 bbox.first(1) + j *
offset(1),
59 bbox.first(2) + k *
offset(2));
60 bounds.emplace_back(min_bound, min_bound +
offset);
68 Eigen::Vector3d TransformLatLonAltToModelCoords(
69 const SimilarityTransform3& tform,
double lat,
double lon,
double alt) {
76 .EllToXYZ({Eigen::Vector3d(lat, lon, 0.0)})[0];
77 tform.TransformPoint(&xyz);
78 xyz(2) = tform.Scale() * alt;
82 void WriteBoundingBox(
const std::string& reconstruction_path,
83 const std::pair<Eigen::Vector3d, Eigen::Vector3d>& bounds,
84 const std::string& suffix =
"") {
85 const Eigen::Vector3d extent = bounds.second - bounds.first;
88 const std::string
path =
89 JoinPaths(reconstruction_path,
"bbox_aligned" + suffix +
".txt");
90 std::ofstream file(
path, std::ios::trunc);
91 CHECK(file.is_open()) <<
path;
95 file << bounds.first.transpose() <<
std::endl;
96 file << bounds.second.transpose() <<
std::endl;
100 const std::string
path =
101 JoinPaths(reconstruction_path,
"bbox_oriented" + suffix +
".txt");
102 std::ofstream file(
path, std::ios::trunc);
103 CHECK(file.is_open()) <<
path;
107 const Eigen::Vector3d center = (bounds.first + bounds.second) * 0.5;
114 void ReadFileCameraLocations(
const std::string& ref_images_path,
115 std::vector<std::string>& ref_image_names,
116 std::vector<Eigen::Vector3d>& ref_locations) {
118 for (
const auto& line : lines) {
119 std::stringstream line_parser(line);
120 std::string image_name;
121 Eigen::Vector3d camera_position;
122 line_parser >> image_name >> camera_position[0] >> camera_position[1] >>
124 ref_image_names.push_back(image_name);
125 ref_locations.push_back(camera_position);
129 void ReadDatabaseCameraLocations(
const std::string& database_path,
130 std::vector<std::string>& ref_image_names,
131 std::vector<Eigen::Vector3d>& ref_locations) {
132 Database database(database_path);
133 auto images = database.ReadAllImages();
134 std::vector<Eigen::Vector3d> gps_locations;
136 for (
const auto&
image : images) {
137 if (
image.HasTvecPrior()) {
138 ref_image_names.push_back(
image.Name());
139 gps_locations.push_back(
image.TvecPrior());
142 ref_locations = gps_transform.EllToXYZ(gps_locations);
145 void WriteComparisonErrorsCSV(
const std::string&
path,
146 const std::vector<double>& rotation_errors,
147 const std::vector<double>& translation_errors,
148 const std::vector<double>& proj_center_errors) {
149 CHECK_EQ(rotation_errors.size(), translation_errors.size());
150 CHECK_EQ(rotation_errors.size(), proj_center_errors.size());
152 std::ofstream file(
path, std::ios::trunc);
153 CHECK(file.is_open()) <<
path;
156 file <<
"# Model comparison pose errors: one entry per common image"
158 file <<
"# <rotation error (deg)>, <translation error>, <proj center error>"
160 for (
size_t i = 0; i < rotation_errors.size(); ++i) {
161 file << rotation_errors[i] <<
", " << translation_errors[i] <<
", "
166 void PrintErrorStats(std::ostream& out, std::vector<double>& vals) {
167 const size_t len = vals.size();
169 out <<
"Cannot extract error statistics from empty input" <<
std::endl;
172 std::sort(vals.begin(), vals.end());
173 out <<
"Min: " << vals.front() <<
std::endl;
174 out <<
"Max: " << vals.back() <<
std::endl;
177 out <<
"P90: " << vals[size_t(0.9 * len)] <<
std::endl;
178 out <<
"P99: " << vals[size_t(0.99 * len)] <<
std::endl;
181 void PrintComparisonSummary(std::ostream& out,
182 std::vector<double>& rotation_errors,
183 std::vector<double>& translation_errors,
184 std::vector<double>& proj_center_errors) {
185 out <<
"# Image pose error summary" <<
std::endl;
187 PrintErrorStats(out, rotation_errors);
189 PrintErrorStats(out, translation_errors);
191 PrintErrorStats(out, proj_center_errors);
197 std::string input_path;
198 std::string output_path;
199 std::string database_path;
200 std::string ref_images_path;
201 std::string transform_path;
202 std::string alignment_type =
"custom";
203 int min_common_images = 3;
204 bool robust_alignment =
true;
205 bool estimate_scale =
true;
215 "{plane, ecef, enu, enu-unscaled, custom}");
221 options.
Parse(argc, argv);
224 const std::unordered_set<std::string> alignment_options{
225 "plane",
"ecef",
"enu",
"enu-unscaled",
"custom"};
226 if (alignment_options.count(alignment_type) == 0) {
227 std::cerr <<
"ERROR: Invalid `alignment_type` - supported values are "
228 "{'plane', 'ecef', 'enu', 'enu-unscaled', 'custom'}"
233 if (robust_alignment && ransac_options.
max_error <= 0) {
234 std::cout <<
"WARNING: You must provide a maximum alignment error > 0"
239 if (alignment_type !=
"plane" && database_path.empty() &&
240 ref_images_path.empty()) {
241 std::cerr <<
"ERROR: Location alignment requires either database or "
242 "location file path."
247 std::vector<std::string> ref_image_names;
248 std::vector<Eigen::Vector3d> ref_locations;
249 if (!ref_images_path.empty() && database_path.empty()) {
250 ReadFileCameraLocations(ref_images_path, ref_image_names, ref_locations);
251 }
else if (!database_path.empty() && ref_images_path.empty()) {
252 ReadDatabaseCameraLocations(database_path, ref_image_names, ref_locations);
254 std::cerr <<
"ERROR: Use location file or database, not both" <<
std::endl;
258 if (alignment_type !=
"plane" &&
259 static_cast<int>(ref_locations.size()) < min_common_images) {
260 std::cout <<
"WARNING: Cannot align with insufficient reference locations."
266 reconstruction.
Read(input_path);
268 bool alignment_success =
true;
270 if (alignment_type ==
"plane") {
275 std::cout <<
StringPrintf(
" => Using %d reference images",
276 ref_image_names.size())
279 if (estimate_scale) {
280 if (robust_alignment) {
282 ref_image_names, ref_locations, min_common_images, ransac_options,
285 alignment_success = reconstruction.
Align(ref_image_names, ref_locations,
286 min_common_images, &tform);
289 if (robust_alignment) {
290 alignment_success = reconstruction.
AlignRobust<
false>(
291 ref_image_names, ref_locations, min_common_images, ransac_options,
294 alignment_success = reconstruction.
Align<
false>(
295 ref_image_names, ref_locations, min_common_images, &tform);
299 std::vector<double> errors;
300 errors.reserve(ref_image_names.size());
302 for (
size_t i = 0; i < ref_image_names.size(); ++i) {
304 if (
image !=
nullptr) {
305 errors.push_back((
image->ProjectionCenter() - ref_locations[i]).norm());
308 std::cout <<
StringPrintf(
" => Alignment error: %f (mean), %f (median)",
315 alignment_type ==
"enu-unscaled");
319 if (alignment_success) {
320 std::cout <<
" => Alignment succeeded" <<
std::endl;
321 reconstruction.
Write(output_path);
322 if (!transform_path.empty()) {
323 tform.
Write(transform_path);
327 std::cout <<
" => Alignment failed" <<
std::endl;
337 options.
Parse(argc, argv);
357 std::cout <<
StringPrintf(
"Mean observations per image: %f",
360 std::cout <<
StringPrintf(
"Mean reprojection error: %fpx",
368 std::string input_path1;
369 std::string input_path2;
370 std::string output_path;
371 double min_inlier_observations = 0.3;
372 double max_reproj_error = 8.0;
378 options.
AddDefaultOption(
"min_inlier_observations", &min_inlier_observations);
380 options.
Parse(argc, argv);
382 if (!output_path.empty() && !
ExistsDir(output_path)) {
383 std::cerr <<
"ERROR: Provided output path is not a valid directory"
389 reconstruction1.
Read(input_path1);
397 reconstruction2.
Read(input_path2);
405 const auto common_image_ids =
407 std::cout <<
StringPrintf(
"Common images: %d", common_image_ids.size())
412 min_inlier_observations,
413 max_reproj_error, &alignment)) {
414 std::cout <<
"=> Reconstruction alignment failed" <<
std::endl;
419 std::cout <<
"Computed alignment transform:" <<
std::endl
422 const size_t num_images = common_image_ids.size();
423 std::vector<double> rotation_errors(num_images, 0.0);
424 std::vector<double> translation_errors(num_images, 0.0);
425 std::vector<double> proj_center_errors(num_images, 0.0);
426 for (
size_t i = 0; i < num_images; ++i) {
427 const image_t image_id = common_image_ids[i];
428 const Image& image1 = reconstruction1.
Image(image_id);
429 Image& image2 = reconstruction2.
Image(image_id);
433 const Eigen::Quaterniond quat1(normalized_qvec1(0), normalized_qvec1(1),
434 normalized_qvec1(2), normalized_qvec1(3));
436 const Eigen::Quaterniond quat2(normalized_qvec2(0), normalized_qvec2(1),
437 normalized_qvec2(2), normalized_qvec2(3));
439 rotation_errors[i] =
RadToDeg(quat1.angularDistance(quat2));
440 translation_errors[i] = (image1.
Tvec() - image2.
Tvec()).norm();
441 proj_center_errors[i] =
445 if (output_path.empty()) {
446 PrintComparisonSummary(std::cout, rotation_errors, translation_errors,
449 const std::string errors_path =
JoinPaths(output_path,
"errors.csv");
450 WriteComparisonErrorsCSV(errors_path, rotation_errors, translation_errors,
452 const std::string summary_path =
453 JoinPaths(output_path,
"errors_summary.txt");
454 std::ofstream file(summary_path, std::ios::trunc);
455 CHECK(file.is_open()) << summary_path;
456 PrintComparisonSummary(file, rotation_errors, translation_errors,
464 std::string input_path;
465 std::string output_path;
466 std::string output_type;
467 bool skip_distortion =
false;
473 "{BIN, TXT, NVM, Bundler, VRML, PLY, R3D, CAM}");
475 options.
Parse(argc, argv);
478 reconstruction.
Read(input_path);
481 if (output_type ==
"bin") {
483 }
else if (output_type ==
"txt") {
485 }
else if (output_type ==
"nvm") {
486 reconstruction.
ExportNVM(output_path, skip_distortion);
487 }
else if (output_type ==
"bundler") {
489 output_path +
".list.txt", skip_distortion);
490 }
else if (output_type ==
"r3d") {
492 }
else if (output_type ==
"cam") {
493 reconstruction.
ExportCam(output_path, skip_distortion);
494 }
else if (output_type ==
"ply") {
496 }
else if (output_type ==
"vrml") {
497 const auto base_path = output_path.substr(0, output_path.find_last_of(
"."));
498 reconstruction.
ExportVRML(base_path +
".images.wrl",
499 base_path +
".points3D.wrl", 1,
500 Eigen::Vector3d(1, 0, 0));
502 std::cerr <<
"ERROR: Invalid `output_type`" <<
std::endl;
513 std::string input_path;
514 std::string output_path;
515 std::string boundary;
516 std::string gps_transform_path;
524 options.
Parse(argc, argv);
527 std::cerr <<
"ERROR: `input_path` is not a directory" <<
std::endl;
532 std::cerr <<
"ERROR: `output_path` is not a directory" <<
std::endl;
536 std::vector<double> boundary_elements = CSVToVector<double>(boundary);
537 if (boundary_elements.size() != 2 && boundary_elements.size() != 6) {
538 std::cerr <<
"ERROR: Invalid `boundary` - supported values are "
539 "'x1,y1,z1,x2,y2,z2' or 'p1,p2'."
545 reconstruction.
Read(input_path);
548 std::pair<Eigen::Vector3d, Eigen::Vector3d> bounding_box;
549 if (boundary_elements.size() == 6) {
551 if (!gps_transform_path.empty()) {
557 is_gps ? TransformLatLonAltToModelCoords(tform, boundary_elements[0],
558 boundary_elements[1],
559 boundary_elements[2])
560 : Eigen::Vector3d(boundary_elements[0], boundary_elements[1],
561 boundary_elements[2]);
562 bounding_box.second =
563 is_gps ? TransformLatLonAltToModelCoords(tform, boundary_elements[3],
564 boundary_elements[4],
565 boundary_elements[5])
566 : Eigen::Vector3d(boundary_elements[3], boundary_elements[4],
567 boundary_elements[5]);
570 boundary_elements[1]);
574 reconstruction.
Crop(bounding_box).
Write(output_path);
575 WriteBoundingBox(output_path, bounding_box);
577 std::cout <<
"=> Cropping succeeded" <<
std::endl;
583 std::string input_path1;
584 std::string input_path2;
585 std::string output_path;
586 double max_reproj_error = 64.0;
593 options.
Parse(argc, argv);
596 reconstruction1.
Read(input_path1);
604 reconstruction2.
Read(input_path2);
612 if (reconstruction1.
Merge(reconstruction2, max_reproj_error)) {
613 std::cout <<
"=> Merge succeeded" <<
std::endl;
620 std::cout <<
"=> Merge failed" <<
std::endl;
623 reconstruction1.
Write(output_path);
629 std::string input_path;
630 std::string output_path;
631 std::string method =
"MANHATTAN-WORLD";
640 "{MANHATTAN-WORLD, IMAGE-ORIENTATION}");
643 options.
Parse(argc, argv);
646 if (method !=
"manhattan-world" && method !=
"image-orientation") {
647 std::cout <<
"WARNING: Invalid `method` - supported values are "
648 "'MANHATTAN-WORLD' or 'IMAGE-ORIENTATION'."
654 reconstruction.
Read(input_path);
658 Eigen::Matrix3d tform;
660 if (method ==
"manhattan-world") {
662 frame_estimation_options, reconstruction, *options.
image_path);
664 if (
frame.col(0).nonZeros() == 0) {
665 std::cout <<
"Only aligning vertical axis" <<
std::endl;
667 }
else if (
frame.col(1).nonZeros() == 0) {
669 std::cout <<
"Only aligning horizontal axis" <<
std::endl;
671 tform =
frame.transpose();
672 std::cout <<
"Aligning horizontal and vertical axes" <<
std::endl;
674 }
else if (method ==
"image-orientation") {
675 const Eigen::Vector3d gravity_axis =
679 LOG(FATAL) <<
"Alignment method not supported";
682 std::cout <<
"Using the rotation matrix:" <<
std::endl;
688 std::cout <<
"Writing aligned reconstruction..." <<
std::endl;
689 reconstruction.
Write(output_path);
698 std::string input_path;
699 std::string output_path;
700 std::string split_type;
701 std::string split_params;
702 std::string gps_transform_path;
703 int num_threads = -1;
704 int min_reg_images = 10;
705 int min_num_points = 100;
706 double overlap_ratio = 0.0;
707 double min_area_ratio = 0.0;
714 "{tiles, extent, parts}");
722 options.
Parse(argc, argv);
725 std::cerr <<
"ERROR: `input_path` is not a directory" <<
std::endl;
730 std::cerr <<
"ERROR: `output_path` is not a directory" <<
std::endl;
734 if (overlap_ratio < 0) {
735 std::cout <<
"WARN: Invalid `overlap_ratio`; resetting to 0" <<
std::endl;
740 std::cout <<
StringPrintf(
" => Using \"%s\" split type", split_type.c_str())
744 reconstruction.
Read(input_path);
747 if (!gps_transform_path.empty()) {
752 const double scale = tform.
Scale();
757 std::vector<std::string> tile_keys;
758 std::vector<std::pair<Eigen::Vector3d, Eigen::Vector3d>> exact_bounds;
760 if (split_type ==
"tiles") {
761 std::ifstream file(split_params);
762 CHECK(file.is_open()) << split_params;
764 double x1, y1, z1, x2, y2, z2;
765 std::string tile_key;
766 std::vector<std::pair<Eigen::Vector3d, Eigen::Vector3d>> bounds;
768 file >> tile_key >> x1 >> y1 >> z1 >> x2 >> y2 >> z2;
769 while (!file.fail()) {
770 tile_keys.push_back(tile_key);
772 exact_bounds.emplace_back(
773 TransformLatLonAltToModelCoords(tform, x1, y1, z1),
774 TransformLatLonAltToModelCoords(tform, x2, y2, z2));
776 exact_bounds.emplace_back(Eigen::Vector3d(x1, y1, z1),
777 Eigen::Vector3d(x2, y2, z2));
779 file >> tile_key >> x1 >> y1 >> z1 >> x2 >> y2 >> z2;
781 }
else if (split_type ==
"extent") {
782 std::vector<double> parts = CSVToVector<double>(split_params);
783 Eigen::Vector3d extent(std::numeric_limits<double>::max(),
784 std::numeric_limits<double>::max(),
785 std::numeric_limits<double>::max());
786 for (
size_t i = 0; i < parts.size(); ++i) {
787 extent(i) = parts[i] * scale;
791 const Eigen::Vector3d full_extent = bbox.second - bbox.first;
793 static_cast<int>(full_extent(0) / extent(0)) + 1,
794 static_cast<int>(full_extent(1) / extent(1)) + 1,
795 static_cast<int>(full_extent(2) / extent(2)) + 1);
797 exact_bounds = ComputeEqualPartsBounds(reconstruction, split);
799 }
else if (split_type ==
"parts") {
800 auto parts = CSVToVector<int>(split_params);
802 for (
size_t i = 0; i < parts.size(); ++i) {
805 std::cerr <<
"ERROR: Cannot split in less than 1 parts for dim " << i
810 exact_bounds = ComputeEqualPartsBounds(reconstruction, split);
812 std::cout <<
"WARNING: Invalid split type: " << split_type <<
std::endl;
816 std::vector<std::pair<Eigen::Vector3d, Eigen::Vector3d>> bounds;
817 for (
const auto& bbox : exact_bounds) {
818 const Eigen::Vector3d padding =
819 (overlap_ratio * (bbox.second - bbox.first));
820 bounds.emplace_back(bbox.first - padding, bbox.second + padding);
824 const size_t num_parts = bounds.size();
825 std::cout <<
StringPrintf(
" => Splitting to %d parts", num_parts)
828 const bool use_tile_keys = split_type ==
"tiles";
830 auto SplitReconstruction = [&](
const int idx) {
833 auto bbox_extent = bounds[idx].second - bounds[idx].first;
835 auto model_extent = model_bbox.second - model_bbox.first;
837 (model_extent(0) * model_extent(1)) / (bbox_extent(0) * bbox_extent(1));
841 const bool include_tile =
842 area_ratio >= min_area_ratio &&
843 tile_num_points >= min_num_points &&
844 tile_recon.
NumRegImages() >=
static_cast<size_t>(min_reg_images);
848 "Writing reconstruction %s with %d images, %d points, "
849 "and %.2f%% area coverage",
853 const std::string reconstruction_path =
JoinPaths(output_path,
name);
855 reconstruction.
Write(reconstruction_path);
856 WriteBoundingBox(reconstruction_path, bounds[idx]);
857 WriteBoundingBox(reconstruction_path, exact_bounds[idx],
"_exact");
861 "Skipping reconstruction %s with %d images, %d points, "
862 "and %.2f%% area coverage",
870 for (
size_t idx = 0; idx < num_parts; ++idx) {
871 thread_pool.
AddTask(SplitReconstruction, idx);
880 std::string input_path;
881 std::string output_path;
882 std::string transform_path;
883 bool is_inverse =
false;
890 options.
Parse(argc, argv);
892 std::cout <<
"Reading points input: " << input_path <<
std::endl;
894 bool is_dense =
false;
899 recon.
Read(input_path);
901 std::cerr <<
"Invalid model input; not a PLY file or sparse reconstruction "
907 std::cout <<
"Reading transform input: " << transform_path <<
std::endl;
913 std::cout <<
"Applying transform to recon with " << recon.
NumPoints3D()
917 std::cout <<
"Writing output: " << output_path <<
std::endl;
921 recon.
Write(output_path);
std::shared_ptr< core::Tensor > image
const Eigen::Vector3d & Tvec() const
const Eigen::Vector4d & Qvec() const
Eigen::Vector3d ProjectionCenter() const
void AddRequiredOption(const std::string &name, T *option, const std::string &help_text="")
void AddDefaultOption(const std::string &name, T *option, const std::string &help_text="")
void Parse(const int argc, char **argv)
std::shared_ptr< std::string > image_path
bool Align(const std::vector< std::string > &image_names, const std::vector< Eigen::Vector3d > &locations, const int min_common_images, SimilarityTransform3 *tform=nullptr)
bool ExportCam(const std::string &path, bool skip_distortion=false) const
Reconstruction Crop(const std::pair< Eigen::Vector3d, Eigen::Vector3d > &bbox) const
double ComputeMeanTrackLength() const
size_t NumRegImages() const
void Transform(const SimilarityTransform3 &tform)
std::pair< Eigen::Vector3d, Eigen::Vector3d > ComputeBoundingBox(const double p0=0.0, const double p1=1.0) const
void Write(const std::string &path) const
std::vector< image_t > FindCommonRegImageIds(const Reconstruction &reconstruction) const
bool ExportNVM(const std::string &path, bool skip_distortion=false) const
size_t ComputeNumObservations() 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
size_t NumPoints3D() const
void WriteBinary(const std::string &path) 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
bool AlignRobust(const std::vector< std::string > &image_names, const std::vector< Eigen::Vector3d > &locations, const int min_common_images, const RANSACOptions &ransac_options, SimilarityTransform3 *tform=nullptr)
const class Image * FindImageWithName(const std::string &name) const
double ComputeMeanReprojectionError() const
double ComputeMeanObservationsPerRegImage() const
void WriteText(const std::string &path) const
size_t NumCameras() const
void ImportPLY(const std::string &path)
void Read(const std::string &path)
auto AddTask(func_t &&f, args_t &&... args) -> std::future< typename std::result_of< func_t(args_t...)>::type >
void PrintMinutes() const
Matrix< double, 3, 4 > Matrix3x4d
QTextStream & endl(QTextStream &stream)
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)
float RadToDeg(const float rad)
int RunModelAligner(int argc, char **argv)
Eigen::Matrix3d RotationFromUnitVectors(const Eigen::Vector3d &vector1, const Eigen::Vector3d &vector2)
void PrintHeading2(const std::string &heading)
int RunModelOrientationAligner(int argc, char **argv)
int RunModelConverter(int argc, char **argv)
int RunModelComparer(int argc, char **argv)
Eigen::Vector4d NormalizeQuaternion(const Eigen::Vector4d &qvec)
bool HasFileExtension(const std::string &file_name, const std::string &ext)
bool StringStartsWith(const std::string &str, const std::string &prefix)
void StringToLower(std::string *str)
double Median(const std::vector< T > &elems)
Eigen::Vector4d RotationMatrixToQuaternion(const Eigen::Matrix3d &rot_mat)
int RunModelCropper(int argc, char **argv)
int RunModelMerger(int argc, char **argv)
std::vector< std::string > ReadTextFileLines(const std::string &path)
bool ExistsDir(const std::string &path)
Eigen::Matrix3d EstimateManhattanWorldFrame(const ManhattanWorldFrameEstimationOptions &options, const Reconstruction &reconstruction, const std::string &image_path)
void CreateDirIfNotExists(const std::string &path)
std::string JoinPaths(T const &... paths)
Eigen::Vector3d EstimateGravityVectorFromImageOrientation(const Reconstruction &reconstruction, const double max_axis_distance)
void PrintHeading1(const std::string &heading)
int RunModelSplitter(int argc, char **argv)
double Mean(const std::vector< T > &elems)
std::string StringPrintf(const char *format,...)
int RunModelAnalyzer(int argc, char **argv)
void AlignToENUPlane(Reconstruction *recon, SimilarityTransform3 *tform, bool unscaled)
int RunModelTransformer(int argc, char **argv)
void AlignToPrincipalPlane(Reconstruction *recon, SimilarityTransform3 *tform)
int GetEffectiveNumThreads(const int num_threads)
Eigen::Matrix< Index, 3, 1 > Vector3i
std::string to_string(const T &n)