ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
undistortion.cc
Go to the documentation of this file.
1 // Copyright (c) 2018, ETH Zurich and UNC Chapel Hill.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 //
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 //
14 // * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of
15 // its contributors may be used to endorse or promote products derived
16 // from this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 // POSSIBILITY OF SUCH DAMAGE.
29 //
30 // Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de)
31 
32 #include "base/undistortion.h"
33 
34 #include <fstream>
35 
36 #include "base/camera_models.h"
37 #include "base/pose.h"
38 #include "base/warp.h"
39 #include "util/logging.h"
40 #include "util/misc.h"
41 
42 namespace colmap {
43 namespace {
44 
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) << " ";
52  }
53  *file << matrix(r, matrix.cols() - 1) << std::endl;
54  }
55 }
56 
57 // Write projection matrix P = K * [R t] to file and prepend given header.
58 void WriteProjectionMatrix(const std::string& path, const Camera& camera,
59  const Image& image, const std::string& header) {
60  CHECK_EQ(camera.ModelId(), PinholeCameraModel::model_id);
61 
62  std::ofstream file(path, std::ios::trunc);
63  CHECK(file.is_open()) << path;
64 
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();
70 
71  const Eigen::Matrix3x4d proj_matrix = calib_matrix * image.ProjectionMatrix();
72 
73  if (!header.empty()) {
74  file << header << std::endl;
75  }
76 
77  WriteMatrix(proj_matrix, &file);
78 }
79 
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) {
86  if (geometric) {
87  *file << indent << "$COLMAP_EXE_PATH/colmap patch_match_stereo \\"
88  << std::endl;
89  *file << indent << " --workspace_path " << workspace_path << " \\"
90  << std::endl;
91  *file << indent << " --workspace_format " << workspace_format << " \\"
92  << std::endl;
93  if (workspace_format == "PMVS") {
94  *file << indent << " --pmvs_option_name " << pmvs_option_name << " \\"
95  << std::endl;
96  }
97  *file << indent << " --PatchMatchStereo.max_image_size 2000 \\"
98  << std::endl;
99  *file << indent << " --PatchMatchStereo.geom_consistency true"
100  << std::endl;
101  } else {
102  *file << indent << "$COLMAP_EXE_PATH/colmap patch_match_stereo \\"
103  << std::endl;
104  *file << indent << " --workspace_path " << workspace_path << " \\"
105  << std::endl;
106  *file << indent << " --workspace_format " << workspace_format << " \\"
107  << std::endl;
108  if (workspace_format == "PMVS") {
109  *file << indent << " --pmvs_option_name " << pmvs_option_name << " \\"
110  << std::endl;
111  }
112  *file << indent << " --PatchMatchStereo.max_image_size 2000 \\"
113  << std::endl;
114  *file << indent << " --PatchMatchStereo.geom_consistency false"
115  << std::endl;
116  }
117 
118  *file << indent << "$COLMAP_EXE_PATH/colmap stereo_fusion \\" << std::endl;
119  *file << indent << " --workspace_path " << workspace_path << " \\"
120  << std::endl;
121  *file << indent << " --workspace_format " << workspace_format << " \\"
122  << std::endl;
123  if (workspace_format == "PMVS") {
124  *file << indent << " --pmvs_option_name " << pmvs_option_name << " \\"
125  << std::endl;
126  }
127  if (geometric) {
128  *file << indent << " --input_type geometric \\" << std::endl;
129  } else {
130  *file << indent << " --input_type photometric \\" << std::endl;
131  }
132  *file << indent << " --output_path "
133  << JoinPaths(workspace_path, output_prefix + "fused.ply") << std::endl;
134 
135  *file << indent << "$COLMAP_EXE_PATH/colmap poisson_mesher \\" << std::endl;
136  *file << indent << " --input_path "
137  << JoinPaths(workspace_path, output_prefix + "fused.ply") << " \\"
138  << std::endl;
139  *file << indent << " --output_path "
140  << JoinPaths(workspace_path, output_prefix + "meshed-poisson.ply")
141  << std::endl;
142 
143  *file << indent << "$COLMAP_EXE_PATH/colmap delaunay_mesher \\" << std::endl;
144  *file << indent << " --input_path "
145  << JoinPaths(workspace_path, output_prefix) << " \\" << std::endl;
146  *file << indent << " --input_type dense " << std::endl;
147  *file << indent << " --output_path "
148  << JoinPaths(workspace_path, output_prefix + "meshed-delaunay.ply")
149  << std::endl;
150 }
151 
152 } // namespace
153 
155  Reconstruction* reconstruction,
156  const std::string& image_path,
157  const std::string& output_path,
158  const int num_patch_match_src_images,
159  const CopyType copy_type,
160  const std::vector<image_t>& image_ids)
161  : options_(options),
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) {}
168 
169 void COLMAPUndistorter::Run() {
170  PrintHeading1("Image undistortion");
171 
172  CreateDirIfNotExists(JoinPaths(output_path_, "images"));
173  CreateDirIfNotExists(JoinPaths(output_path_, "sparse"));
174  CreateDirIfNotExists(JoinPaths(output_path_, "stereo"));
175  CreateDirIfNotExists(JoinPaths(output_path_, "stereo/depth_maps"));
176  CreateDirIfNotExists(JoinPaths(output_path_, "stereo/normal_maps"));
177  CreateDirIfNotExists(JoinPaths(output_path_, "stereo/consistency_graphs"));
178  reconstruction_->CreateImageDirs(JoinPaths(output_path_, "images"));
179  reconstruction_->CreateImageDirs(JoinPaths(output_path_, "stereo/depth_maps"));
180  reconstruction_->CreateImageDirs(
181  JoinPaths(output_path_, "stereo/normal_maps"));
182  reconstruction_->CreateImageDirs(
183  JoinPaths(output_path_, "stereo/consistency_graphs"));
184 
185  ThreadPool thread_pool;
186  std::vector<std::future<bool>> futures;
187  futures.reserve(reconstruction_->NumRegImages());
188  if (image_ids_.empty()) {
189  for (size_t i = 0; i < reconstruction_->NumRegImages(); ++i) {
190  const image_t image_id = reconstruction_->RegImageIds().at(i);
191  futures.push_back(
192  thread_pool.AddTask(&COLMAPUndistorter::Undistort, this, image_id));
193  }
194  } else {
195  for (const image_t image_id : image_ids_) {
196  futures.push_back(
197  thread_pool.AddTask(&COLMAPUndistorter::Undistort, this, image_id));
198  }
199  }
200 
201  // Only use the image names for the successfully undistorted images
202  // when writing the MVS config files
203  image_names_.clear();
204  for (size_t i = 0; i < futures.size(); ++i) {
205  if (IsStopped()) {
206  break;
207  }
208 
209  std::cout << StringPrintf("Undistorting image [%d/%d]", i + 1,
210  futures.size())
211  << std::endl;
212 
213  if (futures[i].get()) {
214  if (image_ids_.empty()) {
215  const image_t image_id = reconstruction_->RegImageIds().at(i);
216  image_names_.push_back(reconstruction_->Image(image_id).Name());
217  } else {
218  image_names_.push_back(reconstruction_->Image(image_ids_[i]).Name());
219  }
220  }
221  }
222 
223  std::cout << "Writing reconstruction..." << std::endl;
224 
225  // Perform undistortion directly on the provided reconstruction pointer
226  // This avoids creating a copy and ensures in-place update
227  UndistortReconstruction(options_, reconstruction_);
228  reconstruction_->Write(JoinPaths(output_path_, "sparse"));
229 
230  std::cout << "Writing configuration..." << std::endl;
231  WritePatchMatchConfig();
232  WriteFusionConfig();
233 
234  std::cout << "Writing scripts..." << std::endl;
235  WriteScript(false);
236  WriteScript(true);
237 
239 }
240 
241 bool COLMAPUndistorter::Undistort(const image_t image_id) const {
242  const Image& image = reconstruction_->Image(image_id);
243 
244  Bitmap distorted_bitmap;
245  Bitmap undistorted_bitmap;
246  const Camera& camera = reconstruction_->Camera(image.CameraId());
247  Camera undistorted_camera;
248 
249  const std::string input_image_path = JoinPaths(image_path_, image.Name());
250  const std::string output_image_path =
251  JoinPaths(output_path_, "images", image.Name());
252 
253  // Check if the image is already undistorted and copy from source if no
254  // scaling is needed
255  if (camera.IsUndistorted() && options_.max_image_size < 0 &&
256  ExistsFile(input_image_path)) {
257  std::cout << "Undistorted image found; copying to location: "
258  << output_image_path << std::endl;
259  FileCopy(input_image_path, output_image_path, copy_type_);
260  return true;
261  }
262 
263  if (!distorted_bitmap.Read(input_image_path)) {
264  std::cerr << "ERROR: Cannot read image at path " << input_image_path
265  << std::endl;
266  return false;
267  }
268 
269  UndistortImage(options_, distorted_bitmap, camera, &undistorted_bitmap,
270  &undistorted_camera);
271  return undistorted_bitmap.Write(output_image_path);
272 }
273 
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_) {
279  file << image_name << std::endl;
280  file << "__auto__, " << num_patch_match_src_images_ << std::endl;
281  }
282 }
283 
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_) {
289  file << image_name << std::endl;
290  }
291 }
292 
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;
299 
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);
303 }
304 
306  Reconstruction* reconstruction,
307  const std::string& image_path,
308  const std::string& output_path)
309  : options_(options),
310  image_path_(image_path),
311  output_path_(output_path),
312  reconstruction_(CHECK_NOTNULL(reconstruction)) {}
313 
314 void PMVSUndistorter::Run() {
315  PrintHeading1("Image undistortion (CMVS/PMVS)");
316 
317  CreateDirIfNotExists(JoinPaths(output_path_, "pmvs"));
318  CreateDirIfNotExists(JoinPaths(output_path_, "pmvs/txt"));
319  CreateDirIfNotExists(JoinPaths(output_path_, "pmvs/visualize"));
320  CreateDirIfNotExists(JoinPaths(output_path_, "pmvs/models"));
321 
322  ThreadPool thread_pool;
323  std::vector<std::future<bool>> futures;
324  futures.reserve(reconstruction_->NumRegImages());
325  for (size_t i = 0; i < reconstruction_->NumRegImages(); ++i) {
326  futures.push_back(
327  thread_pool.AddTask(&PMVSUndistorter::Undistort, this, i));
328  }
329 
330  for (size_t i = 0; i < futures.size(); ++i) {
331  if (IsStopped()) {
332  thread_pool.Stop();
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."
336  << std::endl;
337  break;
338  }
339 
340  std::cout << StringPrintf("Undistorting image [%d/%d]", i + 1,
341  futures.size())
342  << std::endl;
343 
344  futures[i].get();
345  }
346 
347  std::cout << "Writing bundle file..." << std::endl;
348  UndistortReconstruction(options_, reconstruction_);
349  const std::string bundle_path = JoinPaths(output_path_, "pmvs/bundle.rd.out");
350  reconstruction_->ExportBundler(bundle_path,
351  bundle_path + ".list.txt");
352 
353  std::cout << "Writing visibility file..." << std::endl;
354  WriteVisibilityData();
355 
356  std::cout << "Writing option file..." << std::endl;
357  WriteOptionFile();
358 
359  std::cout << "Writing scripts..." << std::endl;
360  WritePMVSScript();
361  WriteCMVSPMVSScript();
362  WriteCOLMAPScript(false);
363  WriteCOLMAPScript(true);
364  WriteCMVSCOLMAPScript(false);
365  WriteCMVSCOLMAPScript(true);
366 
368 }
369 
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 =
374  JoinPaths(output_path_, StringPrintf("pmvs/txt/%08d.txt", reg_image_idx));
375 
376  const image_t image_id = reconstruction_->RegImageIds().at(reg_image_idx);
377  const Image& image = reconstruction_->Image(image_id);
378  const Camera& camera = reconstruction_->Camera(image.CameraId());
379 
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())
385  << std::endl;
386  return false;
387  }
388 
389  Bitmap undistorted_bitmap;
390  Camera undistorted_camera;
391  UndistortImage(options_, distorted_bitmap, camera, &undistorted_bitmap,
392  &undistorted_camera);
393 
394  WriteProjectionMatrix(proj_matrix_path, undistorted_camera, image, "CONTOUR");
395  return undistorted_bitmap.Write(output_image_path);
396 }
397 
398 void PMVSUndistorter::WriteVisibilityData() const {
399  const auto path = JoinPaths(output_path_, "pmvs/vis.dat");
400  std::ofstream file(path, std::ios::trunc);
401  CHECK(file.is_open()) << path;
402 
403  file << "VISDATA" << std::endl;
404  file << reconstruction_->NumRegImages() << std::endl;
405 
406  const std::vector<image_t>& reg_image_ids = reconstruction_->RegImageIds();
407 
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();
413  ++point2D_idx) {
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);
420  }
421  }
422  }
423  }
424 
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());
428 
429  file << i << " " << visible_image_ids.size();
430  for (const image_t visible_image_id : sorted_visible_image_ids) {
431  file << " " << visible_image_id;
432  }
433  file << std::endl;
434  }
435 }
436 
437 void PMVSUndistorter::WritePMVSScript() const {
438  const auto path = JoinPaths(output_path_, "run-pmvs.sh");
439  std::ofstream file(path, std::ios::trunc);
440  CHECK(file.is_open()) << path;
441 
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;
445 }
446 
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;
451 
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"
457  << std::endl;
458  file << "do" << std::endl;
459  file << " option_name=$(basename \"$file_name\")" << std::endl;
460  file << " if [ \"$option_name\" = \"option-all\" ]; then" << std::endl;
461  file << " continue" << std::endl;
462  file << " fi" << std::endl;
463  file << " $PMVS_EXE_PATH/pmvs2 pmvs/ $option_name" << std::endl;
464  file << "done" << std::endl;
465 }
466 
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;
473 
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-",
477  "", &file);
478 }
479 
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;
486 
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"
494  << std::endl;
495  file << "do" << std::endl;
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;
499  file << " continue" << std::endl;
500  file << " fi" << std::endl;
501  file << " rm -rf \"$workspace_path/stereo\"" << std::endl;
502  WriteCOLMAPCommands(geometric, "pmvs", "PMVS", "$option_name",
503  "$option_name-", " ", &file);
504  file << "done" << std::endl;
505 }
506 
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;
511 
512  file << "# Generated by COLMAP - all images, no clustering." << std::endl;
513 
514  file << "level 1" << std::endl;
515  file << "csize 2" << std::endl;
516  file << "threshold 0.7" << std::endl;
517  file << "wsize 7" << std::endl;
518  file << "minImageNum 3" << std::endl;
519  file << "CPU " << std::thread::hardware_concurrency() << std::endl;
520  file << "setEdge 0" << std::endl;
521  file << "useBound 0" << std::endl;
522  file << "useVisData 1" << std::endl;
523  file << "sequence -1" << std::endl;
524  file << "maxAngle 10" << std::endl;
525  file << "quad 2.0" << std::endl;
526 
527  file << "timages " << reconstruction_->NumRegImages();
528  for (size_t i = 0; i < reconstruction_->NumRegImages(); ++i) {
529  file << " " << i;
530  }
531  file << std::endl;
532 
533  file << "oimages 0" << std::endl;
534 }
535 
537  Reconstruction* reconstruction,
538  const std::string& image_path,
539  const std::string& output_path)
540  : options_(options),
541  image_path_(image_path),
542  output_path_(output_path),
543  reconstruction_(CHECK_NOTNULL(reconstruction)) {}
544 
545 void CMPMVSUndistorter::Run() {
546  PrintHeading1("Image undistortion (CMP-MVS)");
547 
548  ThreadPool thread_pool;
549  std::vector<std::future<bool>> futures;
550  futures.reserve(reconstruction_->NumRegImages());
551  for (size_t i = 0; i < reconstruction_->NumRegImages(); ++i) {
552  futures.push_back(
553  thread_pool.AddTask(&CMPMVSUndistorter::Undistort, this, i));
554  }
555 
556  for (size_t i = 0; i < futures.size(); ++i) {
557  if (IsStopped()) {
558  break;
559  }
560 
561  std::cout << StringPrintf("Undistorting image [%d/%d]", i + 1,
562  futures.size())
563  << std::endl;
564 
565  futures[i].get();
566  }
567 
569 }
570 
571 bool CMPMVSUndistorter::Undistort(const size_t reg_image_idx) const {
572  const std::string output_image_path =
573  JoinPaths(output_path_, StringPrintf("%05d.jpg", reg_image_idx + 1));
574  const std::string proj_matrix_path =
575  JoinPaths(output_path_, StringPrintf("%05d_P.txt", reg_image_idx + 1));
576 
577  const image_t image_id = reconstruction_->RegImageIds().at(reg_image_idx);
578  const Image& image = reconstruction_->Image(image_id);
579  const Camera& camera = reconstruction_->Camera(image.CameraId());
580 
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
585  << std::endl;
586  return false;
587  }
588 
589  Bitmap undistorted_bitmap;
590  Camera undistorted_camera;
591  UndistortImage(options_, distorted_bitmap, camera, &undistorted_bitmap,
592  &undistorted_camera);
593 
594  WriteProjectionMatrix(proj_matrix_path, undistorted_camera, image, "CONTOUR");
595  return undistorted_bitmap.Write(output_image_path);
596 }
597 
599  const UndistortCameraOptions& options, const std::string& image_path,
600  const std::string& output_path,
601  const std::vector<std::pair<std::string, Camera>>& image_names_and_cameras)
602  : options_(options),
603  image_path_(image_path),
604  output_path_(output_path),
605  image_names_and_cameras_(image_names_and_cameras) {}
606 
607 void PureImageUndistorter::Run() {
608  PrintHeading1("Image undistortion");
609 
610  CreateDirIfNotExists(output_path_);
611 
612  ThreadPool thread_pool;
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) {
617  futures.push_back(
618  thread_pool.AddTask(&PureImageUndistorter::Undistort, this, i));
619  }
620 
621  for (size_t i = 0; i < futures.size(); ++i) {
622  if (IsStopped()) {
623  break;
624  }
625 
626  std::cout << StringPrintf("Undistorting image [%d/%d]", i + 1,
627  futures.size())
628  << std::endl;
629 
630  futures[i].get();
631  }
632 
634 }
635 
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;
639 
640  const std::string output_image_path = JoinPaths(output_path_, image_name);
641 
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
646  << std::endl;
647  return false;
648  }
649 
650  Bitmap undistorted_bitmap;
651  Camera undistorted_camera;
652  UndistortImage(options_, distorted_bitmap, camera, &undistorted_bitmap,
653  &undistorted_camera);
654 
655  return undistorted_bitmap.Write(output_image_path);
656 }
657 
659  const UndistortCameraOptions& options, Reconstruction* reconstruction,
660  const std::string& image_path, const std::string& output_path,
661  const std::vector<std::pair<image_t, image_t>>& stereo_pairs)
662  : options_(options),
663  image_path_(image_path),
664  output_path_(output_path),
665  stereo_pairs_(stereo_pairs),
666  reconstruction_(CHECK_NOTNULL(reconstruction)) {}
667 
668 void StereoImageRectifier::Run() {
669  PrintHeading1("Stereo rectification");
670 
671  ThreadPool thread_pool;
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,
676  stereo_pair.first,
677  stereo_pair.second));
678  }
679 
680  for (size_t i = 0; i < futures.size(); ++i) {
681  if (IsStopped()) {
682  break;
683  }
684 
685  std::cout << StringPrintf("Rectifying image pair [%d/%d]", i + 1,
686  futures.size())
687  << std::endl;
688 
689  futures[i].get();
690  }
691 
693 }
694 
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());
701 
702  const std::string image_name1 = StringReplace(image1.Name(), "/", "-");
703  const std::string image_name2 = StringReplace(image2.Name(), "/", "-");
704 
705  const std::string stereo_pair_name =
706  StringPrintf("%s-%s", image_name1.c_str(), image_name2.c_str());
707 
708  CreateDirIfNotExists(JoinPaths(output_path_, stereo_pair_name));
709 
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);
714 
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
719  << std::endl;
720  return;
721  }
722 
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
727  << std::endl;
728  return;
729  }
730 
731  Eigen::Vector4d qvec;
732  Eigen::Vector3d tvec;
733  ComputeRelativePose(image1.Qvec(), image1.Tvec(), image2.Qvec(),
734  image2.Tvec(), &qvec, &tvec);
735 
736  Bitmap undistorted_bitmap1;
737  Bitmap undistorted_bitmap2;
738  Camera undistorted_camera;
739  Eigen::Matrix4d Q;
741  options_, distorted_bitmap1, distorted_bitmap2, camera1, camera2, qvec,
742  tvec, &undistorted_bitmap1, &undistorted_bitmap2, &undistorted_camera,
743  &Q);
744 
745  undistorted_bitmap1.Write(output_image1_path);
746  undistorted_bitmap2.Write(output_image2_path);
747 
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);
752 }
753 
755  const Camera& camera) {
756  CHECK_GE(options.blank_pixels, 0);
757  CHECK_LE(options.blank_pixels, 1);
758  CHECK_GT(options.min_scale, 0.0);
759  CHECK_LE(options.min_scale, options.max_scale);
760  CHECK_NE(options.max_image_size, 0);
761  CHECK_GE(options.roi_min_x, 0.0);
762  CHECK_GE(options.roi_min_y, 0.0);
763  CHECK_LE(options.roi_max_x, 1.0);
764  CHECK_LE(options.roi_max_y, 1.0);
765  CHECK_LT(options.roi_min_x, options.roi_max_x);
766  CHECK_LT(options.roi_min_y, options.roi_max_y);
767 
768  Camera undistorted_camera;
769  undistorted_camera.SetModelId(PinholeCameraModel::model_id);
770  undistorted_camera.SetWidth(camera.Width());
771  undistorted_camera.SetHeight(camera.Height());
772 
773  // Copy focal length parameters.
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) {
778  undistorted_camera.SetFocalLengthX(camera.FocalLength());
779  undistorted_camera.SetFocalLengthY(camera.FocalLength());
780  } else if (focal_length_idxs.size() == 2) {
781  undistorted_camera.SetFocalLengthX(camera.FocalLengthX());
782  undistorted_camera.SetFocalLengthY(camera.FocalLengthY());
783  }
784 
785  // Copy principal point parameters.
786  undistorted_camera.SetPrincipalPointX(camera.PrincipalPointX());
787  undistorted_camera.SetPrincipalPointY(camera.PrincipalPointY());
788 
789  // Modify undistorted camera parameters based on ROI if enabled
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();
794 
795  const bool roi_enabled = options.roi_min_x > 0.0 || options.roi_min_y > 0.0 ||
796  options.roi_max_x < 1.0 || options.roi_max_y < 1.0;
797 
798  if (roi_enabled) {
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>(
802  std::round(options.roi_min_y * static_cast<double>(camera.Height())));
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>(
806  std::round(options.roi_max_y * static_cast<double>(camera.Height())));
807 
808  // Make sure that the roi is valid.
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);
813 
814  undistorted_camera.SetWidth(roi_max_x - roi_min_x);
815  undistorted_camera.SetHeight(roi_max_y - roi_min_y);
816 
817  undistorted_camera.SetPrincipalPointX(camera.PrincipalPointX() -
818  static_cast<double>(roi_min_x));
819  undistorted_camera.SetPrincipalPointY(camera.PrincipalPointY() -
820  static_cast<double>(roi_min_y));
821  }
822 
823  // Scale the image such the the boundary of the undistorted image.
824  if (roi_enabled || (camera.ModelId() != SimplePinholeCameraModel::model_id &&
825  camera.ModelId() != PinholeCameraModel::model_id)) {
826  // Determine min/max coordinates along top / bottom image border.
827 
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();
832 
833  for (size_t y = roi_min_y; y < roi_max_y; ++y) {
834  // Left border.
835  const Eigen::Vector2d world_point1 =
836  camera.ImageToWorld(Eigen::Vector2d(0.5, y + 0.5));
837  const Eigen::Vector2d undistorted_point1 =
838  undistorted_camera.WorldToImage(world_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));
841  // Right border.
842  const Eigen::Vector2d world_point2 =
843  camera.ImageToWorld(Eigen::Vector2d(camera.Width() - 0.5, y + 0.5));
844  const Eigen::Vector2d undistorted_point2 =
845  undistorted_camera.WorldToImage(world_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));
848  }
849 
850  // Determine min, max coordinates along left / right image border.
851 
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();
856 
857  for (size_t x = roi_min_x; x < roi_max_x; ++x) {
858  // Top border.
859  const Eigen::Vector2d world_point1 =
860  camera.ImageToWorld(Eigen::Vector2d(x + 0.5, 0.5));
861  const Eigen::Vector2d undistorted_point1 =
862  undistorted_camera.WorldToImage(world_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));
865  // Bottom border.
866  const Eigen::Vector2d world_point2 =
867  camera.ImageToWorld(Eigen::Vector2d(x + 0.5, camera.Height() - 0.5));
868  const Eigen::Vector2d undistorted_point2 =
869  undistorted_camera.WorldToImage(world_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));
872  }
873 
874  const double cx = undistorted_camera.PrincipalPointX();
875  const double cy = undistorted_camera.PrincipalPointY();
876 
877  // Scale such that undistorted image contains all pixels of distorted image.
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));
884 
885  // Scale such that there are no blank pixels in undistorted image.
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));
892 
893  // Interpolate scale according to blank_pixels.
894  double scale_x = 1.0 / (min_scale_x * options.blank_pixels +
895  max_scale_x * (1.0 - options.blank_pixels));
896  double scale_y = 1.0 / (min_scale_y * options.blank_pixels +
897  max_scale_y * (1.0 - options.blank_pixels));
898 
899  // Clip the scaling factors.
900  scale_x = Clip(scale_x, options.min_scale, options.max_scale);
901  scale_y = Clip(scale_y, options.min_scale, options.max_scale);
902 
903  // Scale undistorted camera dimensions.
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())));
910 
911  // Scale the principal point according to the new dimensions of the camera.
912  undistorted_camera.SetPrincipalPointX(
913  undistorted_camera.PrincipalPointX() *
914  static_cast<double>(undistorted_camera.Width()) /
915  static_cast<double>(orig_undistorted_camera_width));
916  undistorted_camera.SetPrincipalPointY(
917  undistorted_camera.PrincipalPointY() *
918  static_cast<double>(undistorted_camera.Height()) /
919  static_cast<double>(orig_undistorted_camera_height));
920  }
921 
922  if (options.max_image_size > 0) {
923  const double max_image_scale_x =
924  options.max_image_size /
925  static_cast<double>(undistorted_camera.Width());
926  const double max_image_scale_y =
927  options.max_image_size /
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);
933  }
934  }
935 
936  return undistorted_camera;
937 }
938 
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());
945 
946  *undistorted_camera = UndistortCamera(options, distorted_camera);
947  undistorted_bitmap->Allocate(static_cast<int>(undistorted_camera->Width()),
948  static_cast<int>(undistorted_camera->Height()),
949  distorted_bitmap.IsRGB());
950  distorted_bitmap.CloneMetadata(undistorted_bitmap);
951 
952  WarpImageBetweenCameras(distorted_camera, *undistorted_camera,
953  distorted_bitmap, undistorted_bitmap);
954 }
955 
957  Reconstruction* reconstruction) {
958  const auto distorted_cameras = reconstruction->Cameras();
959  for (auto& camera : distorted_cameras) {
960  reconstruction->Camera(camera.first) =
961  UndistortCamera(options, camera.second);
962  }
963 
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();
969  ++point2D_idx) {
970  auto& point2D = image.Point2D(point2D_idx);
971  point2D.SetXY(undistorted_camera.WorldToImage(
972  distorted_camera.ImageToWorld(point2D.XY())));
973  }
974  }
975 }
976 
977 void RectifyStereoCameras(const Camera& camera1, const Camera& camera2,
978  const Eigen::Vector4d& qvec,
979  const Eigen::Vector3d& tvec, Eigen::Matrix3d* H1,
980  Eigen::Matrix3d* H2, Eigen::Matrix4d* Q) {
981  CHECK(camera1.ModelId() == SimplePinholeCameraModel::model_id ||
983  CHECK(camera2.ModelId() == SimplePinholeCameraModel::model_id ||
985 
986  // Compute the average rotation between the first and the second camera.
987  Eigen::AngleAxisd rvec(
988  Eigen::Quaterniond(qvec(0), qvec(1), qvec(2), qvec(3)));
989  rvec.angle() *= -0.5;
990 
991  Eigen::Matrix3d R2 = rvec.toRotationMatrix();
992  Eigen::Matrix3d R1 = R2.transpose();
993 
994  // Determine the translation, such that it coincides with the X-axis.
995  Eigen::Vector3d t = R2 * tvec;
996 
997  Eigen::Vector3d x_unit_vector(1, 0, 0);
998  if (t.transpose() * x_unit_vector < 0) {
999  x_unit_vector *= -1;
1000  }
1001 
1002  const Eigen::Vector3d rotation_axis = t.cross(x_unit_vector);
1003 
1004  Eigen::Matrix3d R_x;
1005  if (rotation_axis.norm() < std::numeric_limits<double>::epsilon()) {
1006  R_x = Eigen::Matrix3d::Identity();
1007  } else {
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());
1011  }
1012 
1013  // Apply the X-axis correction.
1014  R1 = R_x * R1;
1015  R2 = R_x * R2;
1016  t = R_x * t;
1017 
1018  // Determine the intrinsic calibration matrix.
1019  Eigen::Matrix3d K = Eigen::Matrix3d::Identity();
1020  K(0, 0) = std::min(camera1.MeanFocalLength(), camera2.MeanFocalLength());
1021  K(1, 1) = K(0, 0);
1022  K(0, 2) = camera1.PrincipalPointX();
1023  K(1, 2) = (camera1.PrincipalPointY() + camera2.PrincipalPointY()) / 2;
1024 
1025  // Compose the homographies.
1026  *H1 = K * R1 * camera1.CalibrationMatrix().inverse();
1027  *H2 = K * R2 * camera2.CalibrationMatrix().inverse();
1028 
1029  // Determine the inverse projection matrix that transforms disparity values
1030  // to 3D world coordinates: [x, y, disparity, 1] * Q = [X, Y, Z, 1] * w.
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);
1036  (*Q)(3, 3) = 0;
1037 }
1038 
1040  const UndistortCameraOptions& options, const Bitmap& distorted_image1,
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());
1050 
1051  *undistorted_camera = UndistortCamera(options, distorted_camera1);
1052  undistorted_image1->Allocate(static_cast<int>(undistorted_camera->Width()),
1053  static_cast<int>(undistorted_camera->Height()),
1054  distorted_image1.IsRGB());
1055  distorted_image1.CloneMetadata(undistorted_image1);
1056 
1057  undistorted_image2->Allocate(static_cast<int>(undistorted_camera->Width()),
1058  static_cast<int>(undistorted_camera->Height()),
1059  distorted_image2.IsRGB());
1060  distorted_image2.CloneMetadata(undistorted_image2);
1061 
1062  Eigen::Matrix3d H1;
1063  Eigen::Matrix3d H2;
1064  RectifyStereoCameras(*undistorted_camera, *undistorted_camera, qvec, tvec,
1065  &H1, &H2, Q);
1066 
1067  WarpImageWithHomographyBetweenCameras(H1.inverse(), distorted_camera1,
1068  *undistorted_camera, distorted_image1,
1069  undistorted_image1);
1070  WarpImageWithHomographyBetweenCameras(H2.inverse(), distorted_camera2,
1071  *undistorted_camera, distorted_image2,
1072  undistorted_image2);
1073 }
1074 
1075 } // namespace colmap
std::shared_ptr< core::Tensor > image
bool IsRGB() const
Definition: bitmap.h:261
bool Allocate(const int width, const int height, const bool as_rgb)
Definition: bitmap.cc:104
void CloneMetadata(Bitmap *target) const
Definition: bitmap.cc:604
int Width() const
Definition: bitmap.h:249
int Height() const
Definition: bitmap.h:250
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)
Definition: camera.h:164
double FocalLengthY() const
Definition: camera.cc:121
Eigen::Vector2d ImageToWorld(const Eigen::Vector2d &image_point) const
Definition: camera.cc:219
double MeanFocalLength() const
Definition: camera.cc:100
int ModelId() const
Definition: camera.h:158
void SetPrincipalPointX(const double ppx)
Definition: camera.cc:158
void SetFocalLengthX(const double focal_length_x)
Definition: camera.cc:134
void Rescale(const double scale)
Definition: camera.cc:237
void SetFocalLengthY(const double focal_length_y)
Definition: camera.cc:140
double FocalLength() const
Definition: camera.cc:109
void SetPrincipalPointY(const double ppy)
Definition: camera.cc:164
Eigen::Matrix3d CalibrationMatrix() const
Definition: camera.cc:75
double FocalLengthX() const
Definition: camera.cc:115
const std::vector< size_t > & FocalLengthIdxs() const
Definition: camera.cc:63
double PrincipalPointX() const
Definition: camera.cc:146
Eigen::Vector2d WorldToImage(const Eigen::Vector2d &world_point) const
Definition: camera.cc:230
size_t Width() const
Definition: camera.h:160
double PrincipalPointY() const
Definition: camera.cc:152
size_t Height() const
Definition: camera.h:162
void SetHeight(const size_t height)
Definition: camera.h:166
void SetModelId(const int model_id)
Definition: camera.cc:51
const std::string & Name() const
Definition: image.h:242
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 >
Definition: threading.h:299
const Timer & GetTimer() const
Definition: threading.cc:154
bool IsStopped()
Definition: threading.cc:97
void PrintMinutes() const
Definition: timer.cc:93
normal_z y
normal_z x
Matrix< double, 3, 4 > Matrix3x4d
Definition: types.h:39
QTextStream & endl(QTextStream &stream)
Definition: QtCompat.h:718
static const std::string path
Definition: PointCloud.cpp:59
void WarpImageBetweenCameras(const Camera &source_camera, const Camera &target_camera, const Bitmap &source_image, Bitmap *target_image)
Definition: warp.cc:51
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)
CopyType
Definition: misc.h:27
void ComputeRelativePose(const Eigen::Vector4d &qvec1, const Eigen::Vector3d &tvec1, const Eigen::Vector4d &qvec2, const Eigen::Vector3d &tvec2, Eigen::Vector4d *qvec12, Eigen::Vector3d *tvec12)
Definition: pose.cc:173
void UndistortImage(const UndistortCameraOptions &options, const Bitmap &distorted_bitmap, const Camera &distorted_camera, Bitmap *undistorted_bitmap, Camera *undistorted_camera)
uint32_t point2D_t
Definition: types.h:67
std::string StringReplace(const std::string &str, const std::string &old_str, const std::string &new_str)
Definition: string.cc:140
bool ExistsFile(const std::string &path)
Definition: misc.cc:100
void CreateDirIfNotExists(const std::string &path)
Definition: misc.cc:112
std::string JoinPaths(T const &... paths)
Definition: misc.h:128
void WarpImageWithHomographyBetweenCameras(const Eigen::Matrix3d &H, const Camera &source_camera, const Camera &target_camera, const Bitmap &source_image, Bitmap *target_image)
Definition: warp.cc:124
void UndistortReconstruction(const UndistortCameraOptions &options, Reconstruction *reconstruction)
Camera UndistortCamera(const UndistortCameraOptions &options, const Camera &camera)
uint32_t image_t
Definition: types.h:61
void PrintHeading1(const std::string &heading)
Definition: misc.cc:225
std::string StringPrintf(const char *format,...)
Definition: string.cc:131
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)
Definition: math.h:167
void FileCopy(const std::string &src_path, const std::string &dst_path, CopyType type)
Definition: misc.cc:85
Eigen::MatrixXd::Index Index
Definition: knncpp.h:26
static const int model_id