45 template <
typename Derived>
46 void WriteMatrix(
const Eigen::MatrixBase<Derived>& matrix,
47 std::ofstream* file) {
49 for (
index_t r = 0; r < matrix.rows(); ++r) {
50 for (
index_t c = 0; c < matrix.cols() - 1; ++c) {
51 *file << matrix(r, c) <<
" ";
53 *file << matrix(r, matrix.cols() - 1) <<
std::endl;
58 void WriteProjectionMatrix(
const std::string&
path,
const Camera& camera,
59 const Image&
image,
const std::string& header) {
62 std::ofstream file(
path, std::ios::trunc);
63 CHECK(file.is_open()) <<
path;
65 Eigen::Matrix3d calib_matrix = Eigen::Matrix3d::Identity();
66 calib_matrix(0, 0) = camera.FocalLengthX();
67 calib_matrix(1, 1) = camera.FocalLengthY();
68 calib_matrix(0, 2) = camera.PrincipalPointX();
69 calib_matrix(1, 2) = camera.PrincipalPointY();
73 if (!header.empty()) {
77 WriteMatrix(proj_matrix, &file);
80 void WriteCOLMAPCommands(
const bool geometric,
81 const std::string& workspace_path,
82 const std::string& workspace_format,
83 const std::string& pmvs_option_name,
84 const std::string& output_prefix,
85 const std::string& indent, std::ofstream* file) {
87 *file << indent <<
"$COLMAP_EXE_PATH/colmap patch_match_stereo \\"
89 *file << indent <<
" --workspace_path " << workspace_path <<
" \\"
91 *file << indent <<
" --workspace_format " << workspace_format <<
" \\"
93 if (workspace_format ==
"PMVS") {
94 *file << indent <<
" --pmvs_option_name " << pmvs_option_name <<
" \\"
97 *file << indent <<
" --PatchMatchStereo.max_image_size 2000 \\"
99 *file << indent <<
" --PatchMatchStereo.geom_consistency true"
102 *file << indent <<
"$COLMAP_EXE_PATH/colmap patch_match_stereo \\"
104 *file << indent <<
" --workspace_path " << workspace_path <<
" \\"
106 *file << indent <<
" --workspace_format " << workspace_format <<
" \\"
108 if (workspace_format ==
"PMVS") {
109 *file << indent <<
" --pmvs_option_name " << pmvs_option_name <<
" \\"
112 *file << indent <<
" --PatchMatchStereo.max_image_size 2000 \\"
114 *file << indent <<
" --PatchMatchStereo.geom_consistency false"
118 *file << indent <<
"$COLMAP_EXE_PATH/colmap stereo_fusion \\" <<
std::endl;
119 *file << indent <<
" --workspace_path " << workspace_path <<
" \\"
121 *file << indent <<
" --workspace_format " << workspace_format <<
" \\"
123 if (workspace_format ==
"PMVS") {
124 *file << indent <<
" --pmvs_option_name " << pmvs_option_name <<
" \\"
128 *file << indent <<
" --input_type geometric \\" <<
std::endl;
130 *file << indent <<
" --input_type photometric \\" <<
std::endl;
132 *file << indent <<
" --output_path "
135 *file << indent <<
"$COLMAP_EXE_PATH/colmap poisson_mesher \\" <<
std::endl;
136 *file << indent <<
" --input_path "
137 <<
JoinPaths(workspace_path, output_prefix +
"fused.ply") <<
" \\"
139 *file << indent <<
" --output_path "
140 <<
JoinPaths(workspace_path, output_prefix +
"meshed-poisson.ply")
143 *file << indent <<
"$COLMAP_EXE_PATH/colmap delaunay_mesher \\" <<
std::endl;
144 *file << indent <<
" --input_path "
146 *file << indent <<
" --input_type dense " <<
std::endl;
147 *file << indent <<
" --output_path "
148 <<
JoinPaths(workspace_path, output_prefix +
"meshed-delaunay.ply")
156 const std::string& image_path,
157 const std::string& output_path,
158 const int num_patch_match_src_images,
160 const std::vector<image_t>& image_ids)
162 image_path_(image_path),
163 output_path_(output_path),
164 copy_type_(copy_type),
165 num_patch_match_src_images_(num_patch_match_src_images),
166 reconstruction_(CHECK_NOTNULL(reconstruction)),
167 image_ids_(image_ids) {}
169 void COLMAPUndistorter::Run() {
181 JoinPaths(output_path_,
"stereo/normal_maps"));
183 JoinPaths(output_path_,
"stereo/consistency_graphs"));
186 std::vector<std::future<bool>> futures;
188 if (image_ids_.empty()) {
189 for (
size_t i = 0; i < reconstruction_->
NumRegImages(); ++i) {
192 thread_pool.
AddTask(&COLMAPUndistorter::Undistort,
this, image_id));
195 for (
const image_t image_id : image_ids_) {
197 thread_pool.
AddTask(&COLMAPUndistorter::Undistort,
this, image_id));
203 image_names_.clear();
204 for (
size_t i = 0; i < futures.size(); ++i) {
209 std::cout <<
StringPrintf(
"Undistorting image [%d/%d]", i + 1,
213 if (futures[i].get()) {
214 if (image_ids_.empty()) {
216 image_names_.push_back(reconstruction_->
Image(image_id).
Name());
218 image_names_.push_back(reconstruction_->
Image(image_ids_[i]).
Name());
223 std::cout <<
"Writing reconstruction..." <<
std::endl;
230 std::cout <<
"Writing configuration..." <<
std::endl;
231 WritePatchMatchConfig();
234 std::cout <<
"Writing scripts..." <<
std::endl;
241 bool COLMAPUndistorter::Undistort(
const image_t image_id)
const {
242 const Image&
image = reconstruction_->
Image(image_id);
244 Bitmap distorted_bitmap;
245 Bitmap undistorted_bitmap;
247 Camera undistorted_camera;
249 const std::string input_image_path =
JoinPaths(image_path_,
image.Name());
250 const std::string output_image_path =
257 std::cout <<
"Undistorted image found; copying to location: "
259 FileCopy(input_image_path, output_image_path, copy_type_);
263 if (!distorted_bitmap.Read(input_image_path)) {
264 std::cerr <<
"ERROR: Cannot read image at path " << input_image_path
269 UndistortImage(options_, distorted_bitmap, camera, &undistorted_bitmap,
270 &undistorted_camera);
271 return undistorted_bitmap.Write(output_image_path);
274 void COLMAPUndistorter::WritePatchMatchConfig()
const {
275 const auto path =
JoinPaths(output_path_,
"stereo/patch-match.cfg");
276 std::ofstream file(
path, std::ios::trunc);
277 CHECK(file.is_open()) <<
path;
278 for (
const auto& image_name : image_names_) {
280 file <<
"__auto__, " << num_patch_match_src_images_ <<
std::endl;
284 void COLMAPUndistorter::WriteFusionConfig()
const {
285 const auto path =
JoinPaths(output_path_,
"stereo/fusion.cfg");
286 std::ofstream file(
path, std::ios::trunc);
287 CHECK(file.is_open()) <<
path;
288 for (
const auto& image_name : image_names_) {
293 void COLMAPUndistorter::WriteScript(
const bool geometric)
const {
294 const std::string
path =
295 JoinPaths(output_path_, geometric ?
"run-colmap-geometric.sh"
296 :
"run-colmap-photometric.sh");
297 std::ofstream file(
path, std::ios::trunc);
298 CHECK(file.is_open()) <<
path;
300 file <<
"# You must set $COLMAP_EXE_PATH to " <<
std::endl
301 <<
"# the directory containing the COLMAP executables." <<
std::endl;
302 WriteCOLMAPCommands(geometric,
".",
"COLMAP",
"option-all",
"",
"", &file);
307 const std::string& image_path,
308 const std::string& output_path)
310 image_path_(image_path),
311 output_path_(output_path),
312 reconstruction_(CHECK_NOTNULL(reconstruction)) {}
314 void PMVSUndistorter::Run() {
323 std::vector<std::future<bool>> futures;
325 for (
size_t i = 0; i < reconstruction_->
NumRegImages(); ++i) {
327 thread_pool.
AddTask(&PMVSUndistorter::Undistort,
this, i));
330 for (
size_t i = 0; i < futures.size(); ++i) {
333 std::cout <<
"WARNING: Stopped the undistortion process. Image point "
334 "locations and camera parameters for not yet processed "
335 "images in the Bundler output file is probably wrong."
340 std::cout <<
StringPrintf(
"Undistorting image [%d/%d]", i + 1,
347 std::cout <<
"Writing bundle file..." <<
std::endl;
349 const std::string bundle_path =
JoinPaths(output_path_,
"pmvs/bundle.rd.out");
351 bundle_path +
".list.txt");
353 std::cout <<
"Writing visibility file..." <<
std::endl;
354 WriteVisibilityData();
356 std::cout <<
"Writing option file..." <<
std::endl;
359 std::cout <<
"Writing scripts..." <<
std::endl;
361 WriteCMVSPMVSScript();
362 WriteCOLMAPScript(
false);
363 WriteCOLMAPScript(
true);
364 WriteCMVSCOLMAPScript(
false);
365 WriteCMVSCOLMAPScript(
true);
370 bool PMVSUndistorter::Undistort(
const size_t reg_image_idx)
const {
371 const std::string output_image_path =
JoinPaths(
372 output_path_,
StringPrintf(
"pmvs/visualize/%08d.jpg", reg_image_idx));
373 const std::string proj_matrix_path =
377 const Image&
image = reconstruction_->
Image(image_id);
380 Bitmap distorted_bitmap;
381 const std::string input_image_path =
JoinPaths(image_path_,
image.Name());
382 if (!distorted_bitmap.Read(input_image_path)) {
383 std::cerr <<
StringPrintf(
"ERROR: Cannot read image at path %s",
384 input_image_path.c_str())
389 Bitmap undistorted_bitmap;
390 Camera undistorted_camera;
391 UndistortImage(options_, distorted_bitmap, camera, &undistorted_bitmap,
392 &undistorted_camera);
394 WriteProjectionMatrix(proj_matrix_path, undistorted_camera,
image,
"CONTOUR");
395 return undistorted_bitmap.Write(output_image_path);
398 void PMVSUndistorter::WriteVisibilityData()
const {
400 std::ofstream file(
path, std::ios::trunc);
401 CHECK(file.is_open()) <<
path;
406 const std::vector<image_t>& reg_image_ids = reconstruction_->
RegImageIds();
408 for (
size_t i = 0; i < reg_image_ids.size(); ++i) {
409 const image_t image_id = reg_image_ids[i];
410 const Image&
image = reconstruction_->
Image(image_id);
411 std::unordered_set<image_t> visible_image_ids;
412 for (
point2D_t point2D_idx = 0; point2D_idx <
image.NumPoints2D();
414 const Point2D& point2D =
image.Point2D(point2D_idx);
415 if (point2D.HasPoint3D()) {
416 const Point3D& point3D = reconstruction_->
Point3D(point2D.Point3DId());
417 for (
const TrackElement& track_el : point3D.Track().Elements()) {
418 if (track_el.image_id != image_id) {
419 visible_image_ids.insert(track_el.image_id);
425 std::vector<image_t> sorted_visible_image_ids(visible_image_ids.begin(),
426 visible_image_ids.end());
427 std::sort(sorted_visible_image_ids.begin(), sorted_visible_image_ids.end());
429 file << i <<
" " << visible_image_ids.size();
430 for (
const image_t visible_image_id : sorted_visible_image_ids) {
431 file <<
" " << visible_image_id;
437 void PMVSUndistorter::WritePMVSScript()
const {
439 std::ofstream file(
path, std::ios::trunc);
440 CHECK(file.is_open()) <<
path;
442 file <<
"# You must set $PMVS_EXE_PATH to " <<
std::endl
443 <<
"# the directory containing the CMVS-PMVS executables." <<
std::endl;
444 file <<
"$PMVS_EXE_PATH/pmvs2 pmvs/ option-all" <<
std::endl;
447 void PMVSUndistorter::WriteCMVSPMVSScript()
const {
448 const auto path =
JoinPaths(output_path_,
"run-cmvs-pmvs.sh");
449 std::ofstream file(
path, std::ios::trunc);
450 CHECK(file.is_open()) <<
path;
452 file <<
"# You must set $PMVS_EXE_PATH to " <<
std::endl
453 <<
"# the directory containing the CMVS-PMVS executables." <<
std::endl;
454 file <<
"$PMVS_EXE_PATH/cmvs pmvs/" <<
std::endl;
455 file <<
"$PMVS_EXE_PATH/genOption pmvs/" <<
std::endl;
456 file <<
"find pmvs/ -iname \"option-*\" | sort | while read file_name"
459 file <<
" option_name=$(basename \"$file_name\")" <<
std::endl;
460 file <<
" if [ \"$option_name\" = \"option-all\" ]; then" <<
std::endl;
463 file <<
" $PMVS_EXE_PATH/pmvs2 pmvs/ $option_name" <<
std::endl;
467 void PMVSUndistorter::WriteCOLMAPScript(
const bool geometric)
const {
468 const std::string
path =
469 JoinPaths(output_path_, geometric ?
"run-colmap-geometric.sh"
470 :
"run-colmap-photometric.sh");
471 std::ofstream file(
path, std::ios::trunc);
472 CHECK(file.is_open()) <<
path;
474 file <<
"# You must set $COLMAP_EXE_PATH to " <<
std::endl
475 <<
"# the directory containing the COLMAP executables." <<
std::endl;
476 WriteCOLMAPCommands(geometric,
"pmvs",
"PMVS",
"option-all",
"option-all-",
480 void PMVSUndistorter::WriteCMVSCOLMAPScript(
const bool geometric)
const {
481 const std::string
path =
482 JoinPaths(output_path_, geometric ?
"run-cmvs-colmap-geometric.sh"
483 :
"run-cmvs-colmap-photometric.sh");
484 std::ofstream file(
path, std::ios::trunc);
485 CHECK(file.is_open()) <<
path;
487 file <<
"# You must set $PMVS_EXE_PATH to " <<
std::endl
488 <<
"# the directory containing the CMVS-PMVS executables" <<
std::endl;
489 file <<
"# and you must set $COLMAP_EXE_PATH to " <<
std::endl
490 <<
"# the directory containing the COLMAP executables." <<
std::endl;
491 file <<
"$PMVS_EXE_PATH/cmvs pmvs/" <<
std::endl;
492 file <<
"$PMVS_EXE_PATH/genOption pmvs/" <<
std::endl;
493 file <<
"find pmvs/ -iname \"option-*\" | sort | while read file_name"
496 file <<
" workspace_path=$(dirname \"$file_name\")" <<
std::endl;
497 file <<
" option_name=$(basename \"$file_name\")" <<
std::endl;
498 file <<
" if [ \"$option_name\" = \"option-all\" ]; then" <<
std::endl;
501 file <<
" rm -rf \"$workspace_path/stereo\"" <<
std::endl;
502 WriteCOLMAPCommands(geometric,
"pmvs",
"PMVS",
"$option_name",
503 "$option_name-",
" ", &file);
507 void PMVSUndistorter::WriteOptionFile()
const {
508 const auto path =
JoinPaths(output_path_,
"pmvs/option-all");
509 std::ofstream file(
path, std::ios::trunc);
510 CHECK(file.is_open()) <<
path;
512 file <<
"# Generated by COLMAP - all images, no clustering." <<
std::endl;
519 file <<
"CPU " << std::thread::hardware_concurrency() <<
std::endl;
528 for (
size_t i = 0; i < reconstruction_->
NumRegImages(); ++i) {
538 const std::string& image_path,
539 const std::string& output_path)
541 image_path_(image_path),
542 output_path_(output_path),
543 reconstruction_(CHECK_NOTNULL(reconstruction)) {}
545 void CMPMVSUndistorter::Run() {
549 std::vector<std::future<bool>> futures;
551 for (
size_t i = 0; i < reconstruction_->
NumRegImages(); ++i) {
553 thread_pool.
AddTask(&CMPMVSUndistorter::Undistort,
this, i));
556 for (
size_t i = 0; i < futures.size(); ++i) {
561 std::cout <<
StringPrintf(
"Undistorting image [%d/%d]", i + 1,
571 bool CMPMVSUndistorter::Undistort(
const size_t reg_image_idx)
const {
572 const std::string output_image_path =
574 const std::string proj_matrix_path =
578 const Image&
image = reconstruction_->
Image(image_id);
581 Bitmap distorted_bitmap;
582 const std::string input_image_path =
JoinPaths(image_path_,
image.Name());
583 if (!distorted_bitmap.Read(input_image_path)) {
584 std::cerr <<
"ERROR: Cannot read image at path " << input_image_path
589 Bitmap undistorted_bitmap;
590 Camera undistorted_camera;
591 UndistortImage(options_, distorted_bitmap, camera, &undistorted_bitmap,
592 &undistorted_camera);
594 WriteProjectionMatrix(proj_matrix_path, undistorted_camera,
image,
"CONTOUR");
595 return undistorted_bitmap.Write(output_image_path);
600 const std::string& output_path,
601 const std::vector<std::pair<std::string, Camera>>& image_names_and_cameras)
603 image_path_(image_path),
604 output_path_(output_path),
605 image_names_and_cameras_(image_names_and_cameras) {}
607 void PureImageUndistorter::Run() {
613 std::vector<std::future<bool>> futures;
614 size_t num_images = image_names_and_cameras_.size();
615 futures.reserve(num_images);
616 for (
size_t i = 0; i < num_images; ++i) {
618 thread_pool.
AddTask(&PureImageUndistorter::Undistort,
this, i));
621 for (
size_t i = 0; i < futures.size(); ++i) {
626 std::cout <<
StringPrintf(
"Undistorting image [%d/%d]", i + 1,
636 bool PureImageUndistorter::Undistort(
const size_t image_idx)
const {
637 const std::string& image_name = image_names_and_cameras_[image_idx].first;
638 const Camera& camera = image_names_and_cameras_[image_idx].second;
640 const std::string output_image_path =
JoinPaths(output_path_, image_name);
642 Bitmap distorted_bitmap;
643 const std::string input_image_path =
JoinPaths(image_path_, image_name);
644 if (!distorted_bitmap.Read(input_image_path)) {
645 std::cerr <<
"ERROR: Cannot read image at path " << input_image_path
650 Bitmap undistorted_bitmap;
651 Camera undistorted_camera;
652 UndistortImage(options_, distorted_bitmap, camera, &undistorted_bitmap,
653 &undistorted_camera);
655 return undistorted_bitmap.Write(output_image_path);
660 const std::string& image_path,
const std::string& output_path,
661 const std::vector<std::pair<image_t, image_t>>& stereo_pairs)
663 image_path_(image_path),
664 output_path_(output_path),
665 stereo_pairs_(stereo_pairs),
666 reconstruction_(CHECK_NOTNULL(reconstruction)) {}
668 void StereoImageRectifier::Run() {
672 std::vector<std::future<void>> futures;
673 futures.reserve(stereo_pairs_.size());
674 for (
const auto& stereo_pair : stereo_pairs_) {
675 futures.push_back(thread_pool.
AddTask(&StereoImageRectifier::Rectify,
this,
677 stereo_pair.second));
680 for (
size_t i = 0; i < futures.size(); ++i) {
685 std::cout <<
StringPrintf(
"Rectifying image pair [%d/%d]", i + 1,
695 void StereoImageRectifier::Rectify(
const image_t image_id1,
696 const image_t image_id2)
const {
697 const Image& image1 = reconstruction_->
Image(image_id1);
698 const Image& image2 = reconstruction_->
Image(image_id2);
699 const Camera& camera1 = reconstruction_->
Camera(image1.CameraId());
700 const Camera& camera2 = reconstruction_->
Camera(image2.CameraId());
702 const std::string image_name1 =
StringReplace(image1.Name(),
"/",
"-");
703 const std::string image_name2 =
StringReplace(image2.Name(),
"/",
"-");
705 const std::string stereo_pair_name =
706 StringPrintf(
"%s-%s", image_name1.c_str(), image_name2.c_str());
710 const std::string output_image1_path =
711 JoinPaths(output_path_, stereo_pair_name, image_name1);
712 const std::string output_image2_path =
713 JoinPaths(output_path_, stereo_pair_name, image_name2);
715 Bitmap distorted_bitmap1;
716 const std::string input_image1_path =
JoinPaths(image_path_, image1.Name());
717 if (!distorted_bitmap1.Read(input_image1_path)) {
718 std::cerr <<
"ERROR: Cannot read image at path " << input_image1_path
723 Bitmap distorted_bitmap2;
724 const std::string input_image2_path =
JoinPaths(image_path_, image2.Name());
725 if (!distorted_bitmap2.Read(input_image2_path)) {
726 std::cerr <<
"ERROR: Cannot read image at path " << input_image2_path
731 Eigen::Vector4d qvec;
732 Eigen::Vector3d tvec;
734 image2.Tvec(), &qvec, &tvec);
736 Bitmap undistorted_bitmap1;
737 Bitmap undistorted_bitmap2;
738 Camera undistorted_camera;
741 options_, distorted_bitmap1, distorted_bitmap2, camera1, camera2, qvec,
742 tvec, &undistorted_bitmap1, &undistorted_bitmap2, &undistorted_camera,
745 undistorted_bitmap1.Write(output_image1_path);
746 undistorted_bitmap2.Write(output_image2_path);
748 const auto Q_path =
JoinPaths(output_path_, stereo_pair_name,
"Q.txt");
749 std::ofstream Q_file(Q_path, std::ios::trunc);
750 CHECK(Q_file.is_open()) << Q_path;
751 WriteMatrix(Q, &Q_file);
768 Camera undistorted_camera;
774 const std::vector<size_t>& focal_length_idxs = camera.
FocalLengthIdxs();
775 CHECK_LE(focal_length_idxs.size(), 2)
776 <<
"Not more than two focal length parameters supported.";
777 if (focal_length_idxs.size() == 1) {
780 }
else if (focal_length_idxs.size() == 2) {
790 size_t roi_min_x = 0;
791 size_t roi_min_y = 0;
792 size_t roi_max_x = camera.
Width();
793 size_t roi_max_y = camera.
Height();
799 roi_min_x =
static_cast<size_t>(
800 std::round(options.
roi_min_x *
static_cast<double>(camera.
Width())));
801 roi_min_y =
static_cast<size_t>(
803 roi_max_x =
static_cast<size_t>(
804 std::round(options.
roi_max_x *
static_cast<double>(camera.
Width())));
805 roi_max_y =
static_cast<size_t>(
809 roi_min_x = std::min(roi_min_x, camera.
Width() - 1);
810 roi_min_y = std::min(roi_min_y, camera.
Height() - 1);
811 roi_max_x = std::max(roi_max_x, roi_min_x + 1);
812 roi_max_y = std::max(roi_max_y, roi_min_y + 1);
814 undistorted_camera.
SetWidth(roi_max_x - roi_min_x);
815 undistorted_camera.
SetHeight(roi_max_y - roi_min_y);
818 static_cast<double>(roi_min_x));
820 static_cast<double>(roi_min_y));
828 double left_min_x = std::numeric_limits<double>::max();
829 double left_max_x = std::numeric_limits<double>::lowest();
830 double right_min_x = std::numeric_limits<double>::max();
831 double right_max_x = std::numeric_limits<double>::lowest();
833 for (
size_t y = roi_min_y;
y < roi_max_y; ++
y) {
835 const Eigen::Vector2d world_point1 =
837 const Eigen::Vector2d undistorted_point1 =
839 left_min_x = std::min(left_min_x, undistorted_point1(0));
840 left_max_x = std::max(left_max_x, undistorted_point1(0));
842 const Eigen::Vector2d world_point2 =
844 const Eigen::Vector2d undistorted_point2 =
846 right_min_x = std::min(right_min_x, undistorted_point2(0));
847 right_max_x = std::max(right_max_x, undistorted_point2(0));
852 double top_min_y = std::numeric_limits<double>::max();
853 double top_max_y = std::numeric_limits<double>::lowest();
854 double bottom_min_y = std::numeric_limits<double>::max();
855 double bottom_max_y = std::numeric_limits<double>::lowest();
857 for (
size_t x = roi_min_x;
x < roi_max_x; ++
x) {
859 const Eigen::Vector2d world_point1 =
861 const Eigen::Vector2d undistorted_point1 =
863 top_min_y = std::min(top_min_y, undistorted_point1(1));
864 top_max_y = std::max(top_max_y, undistorted_point1(1));
866 const Eigen::Vector2d world_point2 =
868 const Eigen::Vector2d undistorted_point2 =
870 bottom_min_y = std::min(bottom_min_y, undistorted_point2(1));
871 bottom_max_y = std::max(bottom_max_y, undistorted_point2(1));
878 const double min_scale_x =
879 std::min(cx / (cx - left_min_x),
880 (undistorted_camera.
Width() - 0.5 - cx) / (right_max_x - cx));
881 const double min_scale_y = std::min(
882 cy / (cy - top_min_y),
883 (undistorted_camera.
Height() - 0.5 - cy) / (bottom_max_y - cy));
886 const double max_scale_x =
887 std::max(cx / (cx - left_max_x),
888 (undistorted_camera.
Width() - 0.5 - cx) / (right_min_x - cx));
889 const double max_scale_y = std::max(
890 cy / (cy - top_max_y),
891 (undistorted_camera.
Height() - 0.5 - cy) / (bottom_min_y - cy));
894 double scale_x = 1.0 / (min_scale_x * options.
blank_pixels +
896 double scale_y = 1.0 / (min_scale_y * options.
blank_pixels +
904 const size_t orig_undistorted_camera_width = undistorted_camera.
Width();
905 const size_t orig_undistorted_camera_height = undistorted_camera.
Height();
906 undistorted_camera.
SetWidth(
static_cast<size_t>(
907 std::max(1.0, scale_x * undistorted_camera.
Width())));
908 undistorted_camera.
SetHeight(
static_cast<size_t>(
909 std::max(1.0, scale_y * undistorted_camera.
Height())));
914 static_cast<double>(undistorted_camera.
Width()) /
915 static_cast<double>(orig_undistorted_camera_width));
918 static_cast<double>(undistorted_camera.
Height()) /
919 static_cast<double>(orig_undistorted_camera_height));
923 const double max_image_scale_x =
925 static_cast<double>(undistorted_camera.
Width());
926 const double max_image_scale_y =
928 static_cast<double>(undistorted_camera.
Height());
929 const double max_image_scale =
930 std::min(max_image_scale_x, max_image_scale_y);
931 if (max_image_scale < 1.0) {
932 undistorted_camera.
Rescale(max_image_scale);
936 return undistorted_camera;
940 const Bitmap& distorted_bitmap,
941 const Camera& distorted_camera,
Bitmap* undistorted_bitmap,
942 Camera* undistorted_camera) {
943 CHECK_EQ(distorted_camera.
Width(), distorted_bitmap.
Width());
944 CHECK_EQ(distorted_camera.
Height(), distorted_bitmap.
Height());
947 undistorted_bitmap->
Allocate(
static_cast<int>(undistorted_camera->
Width()),
948 static_cast<int>(undistorted_camera->
Height()),
949 distorted_bitmap.
IsRGB());
953 distorted_bitmap, undistorted_bitmap);
958 const auto distorted_cameras = reconstruction->
Cameras();
959 for (
auto& camera : distorted_cameras) {
960 reconstruction->
Camera(camera.first) =
964 for (
const auto& distorted_image : reconstruction->
Images()) {
965 auto&
image = reconstruction->
Image(distorted_image.first);
966 const auto& distorted_camera = distorted_cameras.at(
image.CameraId());
967 const auto& undistorted_camera = reconstruction->
Camera(
image.CameraId());
968 for (
point2D_t point2D_idx = 0; point2D_idx <
image.NumPoints2D();
970 auto& point2D =
image.Point2D(point2D_idx);
971 point2D.SetXY(undistorted_camera.WorldToImage(
972 distorted_camera.ImageToWorld(point2D.XY())));
978 const Eigen::Vector4d& qvec,
979 const Eigen::Vector3d& tvec, Eigen::Matrix3d* H1,
980 Eigen::Matrix3d* H2, Eigen::Matrix4d* Q) {
987 Eigen::AngleAxisd rvec(
988 Eigen::Quaterniond(qvec(0), qvec(1), qvec(2), qvec(3)));
989 rvec.angle() *= -0.5;
991 Eigen::Matrix3d R2 = rvec.toRotationMatrix();
992 Eigen::Matrix3d R1 = R2.transpose();
995 Eigen::Vector3d t = R2 * tvec;
997 Eigen::Vector3d x_unit_vector(1, 0, 0);
998 if (t.transpose() * x_unit_vector < 0) {
1002 const Eigen::Vector3d rotation_axis = t.cross(x_unit_vector);
1004 Eigen::Matrix3d R_x;
1005 if (rotation_axis.norm() < std::numeric_limits<double>::epsilon()) {
1006 R_x = Eigen::Matrix3d::Identity();
1008 const double angle = std::acos(std::abs(t.transpose() * x_unit_vector) /
1009 (t.norm() * x_unit_vector.norm()));
1010 R_x = Eigen::AngleAxisd(angle, rotation_axis.normalized());
1019 Eigen::Matrix3d K = Eigen::Matrix3d::Identity();
1031 *Q = Eigen::Matrix4d::Identity();
1032 (*Q)(3, 0) = -K(1, 2);
1033 (*Q)(3, 1) = -K(0, 2);
1034 (*Q)(3, 2) = K(0, 0);
1035 (*Q)(2, 3) = -1 / t(0);
1041 const Bitmap& distorted_image2,
const Camera& distorted_camera1,
1042 const Camera& distorted_camera2,
const Eigen::Vector4d& qvec,
1043 const Eigen::Vector3d& tvec,
Bitmap* undistorted_image1,
1044 Bitmap* undistorted_image2,
Camera* undistorted_camera,
1045 Eigen::Matrix4d* Q) {
1046 CHECK_EQ(distorted_camera1.
Width(), distorted_image1.
Width());
1047 CHECK_EQ(distorted_camera1.
Height(), distorted_image1.
Height());
1048 CHECK_EQ(distorted_camera2.
Width(), distorted_image2.
Width());
1049 CHECK_EQ(distorted_camera2.
Height(), distorted_image2.
Height());
1052 undistorted_image1->
Allocate(
static_cast<int>(undistorted_camera->
Width()),
1053 static_cast<int>(undistorted_camera->
Height()),
1054 distorted_image1.
IsRGB());
1057 undistorted_image2->
Allocate(
static_cast<int>(undistorted_camera->
Width()),
1058 static_cast<int>(undistorted_camera->
Height()),
1059 distorted_image2.
IsRGB());
1068 *undistorted_camera, distorted_image1,
1069 undistorted_image1);
1071 *undistorted_camera, distorted_image2,
1072 undistorted_image2);
std::shared_ptr< core::Tensor > image
bool Allocate(const int width, const int height, const bool as_rgb)
void CloneMetadata(Bitmap *target) const
CMPMVSUndistorter(const UndistortCameraOptions &options, Reconstruction *reconstruction, const std::string &image_path, const std::string &output_path)
COLMAPUndistorter(const UndistortCameraOptions &options, Reconstruction *reconstruction, const std::string &image_path, const std::string &output_path, const int num_related_images=20, const CopyType copy_type=CopyType::COPY, const std::vector< image_t > &image_ids=std::vector< image_t >())
void SetWidth(const size_t width)
double FocalLengthY() const
Eigen::Vector2d ImageToWorld(const Eigen::Vector2d &image_point) const
double MeanFocalLength() const
void SetPrincipalPointX(const double ppx)
void SetFocalLengthX(const double focal_length_x)
void Rescale(const double scale)
void SetFocalLengthY(const double focal_length_y)
double FocalLength() const
void SetPrincipalPointY(const double ppy)
Eigen::Matrix3d CalibrationMatrix() const
double FocalLengthX() const
const std::vector< size_t > & FocalLengthIdxs() const
double PrincipalPointX() const
Eigen::Vector2d WorldToImage(const Eigen::Vector2d &world_point) const
double PrincipalPointY() const
void SetHeight(const size_t height)
void SetModelId(const int model_id)
const std::string & Name() const
PMVSUndistorter(const UndistortCameraOptions &options, Reconstruction *reconstruction, const std::string &image_path, const std::string &output_path)
PureImageUndistorter(const UndistortCameraOptions &options, const std::string &image_path, const std::string &output_path, const std::vector< std::pair< std::string, Camera >> &image_names_and_cameras)
size_t NumRegImages() const
void Write(const std::string &path) const
const std::unordered_map< image_t, class Image > & Images() const
const class Image & Image(const image_t image_id) const
const class Camera & Camera(const camera_t camera_id) 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
const std::vector< image_t > & RegImageIds() const
const class Point3D & Point3D(const point3D_t point3D_id) const
StereoImageRectifier(const UndistortCameraOptions &options, Reconstruction *reconstruction, const std::string &image_path, const std::string &output_path, const std::vector< std::pair< image_t, image_t >> &stereo_pairs)
auto AddTask(func_t &&f, args_t &&... args) -> std::future< typename std::result_of< func_t(args_t...)>::type >
const Timer & GetTimer() const
void PrintMinutes() const
Matrix< double, 3, 4 > Matrix3x4d
QTextStream & endl(QTextStream &stream)
static const std::string path
void WarpImageBetweenCameras(const Camera &source_camera, const Camera &target_camera, const Bitmap &source_image, Bitmap *target_image)
void RectifyStereoCameras(const Camera &camera1, const Camera &camera2, const Eigen::Vector4d &qvec, const Eigen::Vector3d &tvec, Eigen::Matrix3d *H1, Eigen::Matrix3d *H2, Eigen::Matrix4d *Q)
void ComputeRelativePose(const Eigen::Vector4d &qvec1, const Eigen::Vector3d &tvec1, const Eigen::Vector4d &qvec2, const Eigen::Vector3d &tvec2, Eigen::Vector4d *qvec12, Eigen::Vector3d *tvec12)
void UndistortImage(const UndistortCameraOptions &options, const Bitmap &distorted_bitmap, const Camera &distorted_camera, Bitmap *undistorted_bitmap, Camera *undistorted_camera)
std::string StringReplace(const std::string &str, const std::string &old_str, const std::string &new_str)
bool ExistsFile(const std::string &path)
void CreateDirIfNotExists(const std::string &path)
std::string JoinPaths(T const &... paths)
void WarpImageWithHomographyBetweenCameras(const Eigen::Matrix3d &H, const Camera &source_camera, const Camera &target_camera, const Bitmap &source_image, Bitmap *target_image)
void UndistortReconstruction(const UndistortCameraOptions &options, Reconstruction *reconstruction)
Camera UndistortCamera(const UndistortCameraOptions &options, const Camera &camera)
void PrintHeading1(const std::string &heading)
std::string StringPrintf(const char *format,...)
void RectifyAndUndistortStereoImages(const UndistortCameraOptions &options, const Bitmap &distorted_image1, const Bitmap &distorted_image2, const Camera &distorted_camera1, const Camera &distorted_camera2, const Eigen::Vector4d &qvec, const Eigen::Vector3d &tvec, Bitmap *undistorted_image1, Bitmap *undistorted_image2, Camera *undistorted_camera, Eigen::Matrix4d *Q)
T Clip(const T &value, const T &low, const T &high)
void FileCopy(const std::string &src_path, const std::string &dst_path, CopyType type)
Eigen::MatrixXd::Index Index
static const int model_id
static const int model_id