ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
image.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 "exe/image.h"
33 
34 #include "base/reconstruction.h"
35 #include "base/undistortion.h"
38 #include "sfm/incremental_mapper.h"
39 #include "util/misc.h"
40 #include "util/option_manager.h"
41 
42 namespace colmap {
43 namespace {
44 
45 // Read stereo image pair names from a text file. The text file is expected to
46 // have one image pair per line, e.g.:
47 //
48 // image_name1.jpg image_name2.jpg
49 // image_name3.jpg image_name4.jpg
50 // image_name5.jpg image_name6.jpg
51 // ...
52 //
53 std::vector<std::pair<image_t, image_t>> ReadStereoImagePairs(
54  const std::string& path, const Reconstruction& reconstruction) {
55  const std::vector<std::string> stereo_pair_lines = ReadTextFileLines(path);
56 
57  std::vector<std::pair<image_t, image_t>> stereo_pairs;
58  stereo_pairs.reserve(stereo_pair_lines.size());
59 
60  for (const auto& line : stereo_pair_lines) {
61  const std::vector<std::string> names = StringSplit(line, " ");
62  CHECK_EQ(names.size(), 2);
63 
64  const Image* image1 = reconstruction.FindImageWithName(names[0]);
65  const Image* image2 = reconstruction.FindImageWithName(names[1]);
66 
67  CHECK_NOTNULL(image1);
68  CHECK_NOTNULL(image2);
69 
70  stereo_pairs.emplace_back(image1->ImageId(), image2->ImageId());
71  }
72 
73  return stereo_pairs;
74 }
75 
76 } // namespace
77 
78 int RunImageDeleter(int argc, char** argv) {
79  std::string input_path;
80  std::string output_path;
81  std::string image_ids_path;
82  std::string image_names_path;
83 
84  OptionManager options;
85  options.AddRequiredOption("input_path", &input_path);
86  options.AddRequiredOption("output_path", &output_path);
87  options.AddDefaultOption(
88  "image_ids_path", &image_ids_path,
89  "Path to text file containing one image_id to delete per line");
90  options.AddDefaultOption(
91  "image_names_path", &image_names_path,
92  "Path to text file containing one image name to delete per line");
93  options.Parse(argc, argv);
94 
95  Reconstruction reconstruction;
96  reconstruction.Read(input_path);
97 
98  if (!image_ids_path.empty()) {
99  const auto image_ids = ReadTextFileLines(image_ids_path);
100 
101  for (const auto& image_id_str : image_ids) {
102  if (image_id_str.empty()) {
103  continue;
104  }
105 
106  const image_t image_id = std::stoi(image_id_str);
107  if (reconstruction.ExistsImage(image_id)) {
108  const auto& image = reconstruction.Image(image_id);
109  std::cout
110  << StringPrintf(
111  "Deleting image_id=%d, image_name=%s from reconstruction",
112  image.ImageId(), image.Name().c_str())
113  << std::endl;
114  reconstruction.DeRegisterImage(image_id);
115  } else {
116  std::cout << StringPrintf(
117  "WARNING: Skipping image_id=%s, because it does not "
118  "exist in the reconstruction",
119  image_id_str.c_str())
120  << std::endl;
121  }
122  }
123  }
124 
125  if (!image_names_path.empty()) {
126  const auto image_names = ReadTextFileLines(image_names_path);
127 
128  for (const auto& image_name : image_names) {
129  if (image_name.empty()) {
130  continue;
131  }
132 
133  const Image* image = reconstruction.FindImageWithName(image_name);
134  if (image != nullptr) {
135  std::cout
136  << StringPrintf(
137  "Deleting image_id=%d, image_name=%s from reconstruction",
138  image->ImageId(), image->Name().c_str())
139  << std::endl;
140  reconstruction.DeRegisterImage(image->ImageId());
141  } else {
142  std::cout << StringPrintf(
143  "WARNING: Skipping image_name=%s, because it does not "
144  "exist in the reconstruction",
145  image_name.c_str())
146  << std::endl;
147  }
148  }
149  }
150 
151  reconstruction.Write(output_path);
152 
153  return EXIT_SUCCESS;
154 }
155 
156 int RunImageFilterer(int argc, char** argv) {
157  std::string input_path;
158  std::string output_path;
159  double min_focal_length_ratio = 0.1;
160  double max_focal_length_ratio = 10.0;
161  double max_extra_param = 100.0;
162  size_t min_num_observations = 10;
163 
164  OptionManager options;
165  options.AddRequiredOption("input_path", &input_path);
166  options.AddRequiredOption("output_path", &output_path);
167  options.AddDefaultOption("min_focal_length_ratio", &min_focal_length_ratio);
168  options.AddDefaultOption("max_focal_length_ratio", &max_focal_length_ratio);
169  options.AddDefaultOption("max_extra_param", &max_extra_param);
170  options.AddDefaultOption("min_num_observations", &min_num_observations);
171  options.Parse(argc, argv);
172 
173  Reconstruction reconstruction;
174  reconstruction.Read(input_path);
175 
176  const size_t num_reg_images = reconstruction.NumRegImages();
177 
178  reconstruction.FilterImages(min_focal_length_ratio, max_focal_length_ratio,
179  max_extra_param);
180 
181  std::vector<image_t> filtered_image_ids;
182  for (const auto& image : reconstruction.Images()) {
183  if (image.second.IsRegistered() &&
184  image.second.NumPoints3D() < min_num_observations) {
185  filtered_image_ids.push_back(image.first);
186  }
187  }
188 
189  for (const auto image_id : filtered_image_ids) {
190  reconstruction.DeRegisterImage(image_id);
191  }
192 
193  const size_t num_filtered_images =
194  num_reg_images - reconstruction.NumRegImages();
195 
196  std::cout << StringPrintf("Filtered %d images from a total of %d images",
197  num_filtered_images, num_reg_images)
198  << std::endl;
199 
200  reconstruction.Write(output_path);
201 
202  return EXIT_SUCCESS;
203 }
204 
205 int RunImageRectifier(int argc, char** argv) {
206  std::string input_path;
207  std::string output_path;
208  std::string stereo_pairs_list;
209 
210  UndistortCameraOptions undistort_camera_options;
211 
212  OptionManager options;
213  options.AddImageOptions();
214  options.AddRequiredOption("input_path", &input_path);
215  options.AddRequiredOption("output_path", &output_path);
216  options.AddRequiredOption("stereo_pairs_list", &stereo_pairs_list);
217  options.AddDefaultOption("blank_pixels",
218  &undistort_camera_options.blank_pixels);
219  options.AddDefaultOption("min_scale", &undistort_camera_options.min_scale);
220  options.AddDefaultOption("max_scale", &undistort_camera_options.max_scale);
221  options.AddDefaultOption("max_image_size",
222  &undistort_camera_options.max_image_size);
223  options.Parse(argc, argv);
224 
225  Reconstruction reconstruction;
226  reconstruction.Read(input_path);
227 
228  const auto stereo_pairs =
229  ReadStereoImagePairs(stereo_pairs_list, reconstruction);
230 
231  StereoImageRectifier rectifier(undistort_camera_options, &reconstruction,
232  *options.image_path, output_path,
233  stereo_pairs);
234  rectifier.Start();
235  rectifier.Wait();
236 
237  return EXIT_SUCCESS;
238 }
239 
240 int RunImageRegistrator(int argc, char** argv) {
241  std::string input_path;
242  std::string output_path;
243 
244  OptionManager options;
245  options.AddDatabaseOptions();
246  options.AddRequiredOption("input_path", &input_path);
247  options.AddRequiredOption("output_path", &output_path);
248  options.AddMapperOptions();
249  options.Parse(argc, argv);
250 
251  if (!ExistsDir(input_path)) {
252  std::cerr << "ERROR: `input_path` is not a directory" << std::endl;
253  return EXIT_FAILURE;
254  }
255 
256  if (!ExistsDir(output_path)) {
257  std::cerr << "ERROR: `output_path` is not a directory" << std::endl;
258  return EXIT_FAILURE;
259  }
260 
261  PrintHeading1("Loading database");
262 
263  DatabaseCache database_cache;
264 
265  {
266  Database database(*options.database_path);
267  Timer timer;
268  timer.Start();
269  const size_t min_num_matches =
270  static_cast<size_t>(options.mapper->min_num_matches);
271  database_cache.Load(database, min_num_matches,
272  options.mapper->ignore_watermarks,
273  options.mapper->image_names);
274  std::cout << std::endl;
275  timer.PrintMinutes();
276  }
277 
278  std::cout << std::endl;
279 
280  Reconstruction reconstruction;
281  reconstruction.Read(input_path);
282 
283  IncrementalMapper mapper(&database_cache);
284  mapper.BeginReconstruction(&reconstruction);
285 
286  const auto mapper_options = options.mapper->Mapper();
287 
288  for (const auto& image : reconstruction.Images()) {
289  if (image.second.IsRegistered()) {
290  continue;
291  }
292 
293  PrintHeading1("Registering image #" + std::to_string(image.first) + " (" +
294  std::to_string(reconstruction.NumRegImages() + 1) + ")");
295 
296  std::cout << " => Image sees " << image.second.NumVisiblePoints3D()
297  << " / " << image.second.NumObservations() << " points"
298  << std::endl;
299 
300  mapper.RegisterNextImage(mapper_options, image.first);
301  }
302 
303  const bool kDiscardReconstruction = false;
304  mapper.EndReconstruction(kDiscardReconstruction);
305 
306  reconstruction.Write(output_path);
307 
308  return EXIT_SUCCESS;
309 }
310 
311 int RunImageUndistorter(int argc, char** argv) {
312  std::string input_path;
313  std::string output_path;
314  std::string output_type = "COLMAP";
315  std::string image_list_path;
316  std::string copy_policy = "copy";
317  int num_patch_match_src_images = 20;
318  CopyType copy_type;
319 
320  UndistortCameraOptions undistort_camera_options;
321 
322  OptionManager options;
323  options.AddImageOptions();
324  options.AddRequiredOption("input_path", &input_path);
325  options.AddRequiredOption("output_path", &output_path);
326  options.AddDefaultOption("output_type", &output_type,
327  "{COLMAP, PMVS, CMP-MVS}");
328  options.AddDefaultOption("image_list_path", &image_list_path);
329  options.AddDefaultOption("copy_policy", &copy_policy,
330  "{copy, soft-link, hard-link}");
331  options.AddDefaultOption("num_patch_match_src_images",
332  &num_patch_match_src_images);
333  options.AddDefaultOption("blank_pixels",
334  &undistort_camera_options.blank_pixels);
335  options.AddDefaultOption("min_scale", &undistort_camera_options.min_scale);
336  options.AddDefaultOption("max_scale", &undistort_camera_options.max_scale);
337  options.AddDefaultOption("max_image_size",
338  &undistort_camera_options.max_image_size);
339  options.AddDefaultOption("roi_min_x", &undistort_camera_options.roi_min_x);
340  options.AddDefaultOption("roi_min_y", &undistort_camera_options.roi_min_y);
341  options.AddDefaultOption("roi_max_x", &undistort_camera_options.roi_max_x);
342  options.AddDefaultOption("roi_max_y", &undistort_camera_options.roi_max_y);
343  options.Parse(argc, argv);
344 
345  CreateDirIfNotExists(output_path);
346 
347  PrintHeading1("Reading reconstruction");
348  Reconstruction reconstruction;
349  reconstruction.Read(input_path);
350  std::cout << StringPrintf(" => Reconstruction with %d images and %d points",
351  reconstruction.NumImages(),
352  reconstruction.NumPoints3D())
353  << std::endl;
354 
355  std::vector<image_t> image_ids;
356  if (!image_list_path.empty()) {
357  const auto& image_names = ReadTextFileLines(image_list_path);
358  for (const auto& image_name : image_names) {
359  const Image* image = reconstruction.FindImageWithName(image_name);
360  if (image != nullptr) {
361  image_ids.push_back(image->ImageId());
362  } else {
363  std::cout << "WARN: Cannot find image " << image_name << std::endl;
364  }
365  }
366  }
367 
368  StringToLower(&copy_policy);
369  if (copy_policy == "copy") {
370  copy_type = CopyType::COPY;
371  } else if (copy_policy == "soft-link") {
372  copy_type = CopyType::SOFT_LINK;
373  } else if (copy_policy == "hard-link") {
374  copy_type = CopyType::HARD_LINK;
375  } else {
376  std::cerr << "ERROR: Invalid `copy_policy` - supported values are "
377  "{'copy', 'soft-link', 'hard-link'}."
378  << std::endl;
379  return EXIT_FAILURE;
380  }
381 
382  std::unique_ptr<Thread> undistorter;
383  if (output_type == "COLMAP") {
384  undistorter.reset(new COLMAPUndistorter(
385  undistort_camera_options, &reconstruction, *options.image_path,
386  output_path, num_patch_match_src_images, copy_type, image_ids));
387  } else if (output_type == "PMVS") {
388  undistorter.reset(new PMVSUndistorter(undistort_camera_options,
389  &reconstruction, *options.image_path,
390  output_path));
391  } else if (output_type == "CMP-MVS") {
392  undistorter.reset(new CMPMVSUndistorter(undistort_camera_options,
393  &reconstruction, *options.image_path,
394  output_path));
395  } else {
396  std::cerr << "ERROR: Invalid `output_type` - supported values are "
397  "{'COLMAP', 'PMVS', 'CMP-MVS'}."
398  << std::endl;
399  return EXIT_FAILURE;
400  }
401 
402  undistorter->Start();
403  undistorter->Wait();
404 
405  return EXIT_SUCCESS;
406 }
407 
408 int RunImageUndistorterStandalone(int argc, char** argv) {
409  std::string input_file;
410  std::string output_path;
411 
412  UndistortCameraOptions undistort_camera_options;
413 
414  OptionManager options;
415  options.AddImageOptions();
416  options.AddRequiredOption("input_file", &input_file);
417  options.AddRequiredOption("output_path", &output_path);
418  options.AddDefaultOption("blank_pixels",
419  &undistort_camera_options.blank_pixels);
420  options.AddDefaultOption("min_scale", &undistort_camera_options.min_scale);
421  options.AddDefaultOption("max_scale", &undistort_camera_options.max_scale);
422  options.AddDefaultOption("max_image_size",
423  &undistort_camera_options.max_image_size);
424  options.AddDefaultOption("roi_min_x", &undistort_camera_options.roi_min_x);
425  options.AddDefaultOption("roi_min_y", &undistort_camera_options.roi_min_y);
426  options.AddDefaultOption("roi_max_x", &undistort_camera_options.roi_max_x);
427  options.AddDefaultOption("roi_max_y", &undistort_camera_options.roi_max_y);
428  options.Parse(argc, argv);
429 
430  CreateDirIfNotExists(output_path);
431 
432  // Loads a text file containing the image names and camera information.
433  // The format of the text file is
434  // image_name CAMERA_MODEL camera_params
435  std::vector<std::pair<std::string, Camera>> image_names_and_cameras;
436 
437  {
438  std::ifstream file(input_file);
439  CHECK(file.is_open()) << input_file;
440 
441  std::string line;
442  std::vector<std::string> lines;
443  while (std::getline(file, line)) {
444  StringTrim(&line);
445 
446  if (line.empty()) {
447  continue;
448  }
449 
450  std::string item;
451  std::stringstream line_stream(line);
452 
453  // Loads the image name.
454  std::string image_name;
455  std::getline(line_stream, image_name, ' ');
456 
457  // Loads the camera and its parameters
458  class Camera camera;
459 
460  std::getline(line_stream, item, ' ');
461  if (!ExistsCameraModelWithName(item)) {
462  std::cerr << "ERROR: Camera model " << item << " does not exist"
463  << std::endl;
464  return EXIT_FAILURE;
465  }
466  camera.SetModelIdFromName(item);
467 
468  std::getline(line_stream, item, ' ');
469  camera.SetWidth(std::stoll(item));
470 
471  std::getline(line_stream, item, ' ');
472  camera.SetHeight(std::stoll(item));
473 
474  camera.Params().clear();
475  while (!line_stream.eof()) {
476  std::getline(line_stream, item, ' ');
477  camera.Params().push_back(std::stold(item));
478  }
479 
480  CHECK(camera.VerifyParams());
481 
482  image_names_and_cameras.emplace_back(image_name, camera);
483  }
484  }
485 
486  std::unique_ptr<Thread> undistorter;
487  undistorter.reset(new PureImageUndistorter(undistort_camera_options,
488  *options.image_path, output_path,
489  image_names_and_cameras));
490 
491  undistorter->Start();
492  undistorter->Wait();
493 
494  return EXIT_SUCCESS;
495 }
496 
497 int RunImageTexturer(int argc, char** argv) {
498  std::string input_path;
499  std::string output_path;
500  std::string mesh_path;
501 
502  OptionManager options;
503  options.AddRequiredOption("input_path", &input_path);
504  options.AddRequiredOption("output_path", &output_path);
505  options.AddRequiredOption("mesh_path", &mesh_path);
506  options.AddTexturingOptions();
507  options.Parse(argc, argv);
508 
509  if (!ExistsDir(input_path)) {
510  std::cerr << "ERROR: `input_path` does not exist" << std::endl;
511  return EXIT_FAILURE;
512  }
513 
514  if (!ExistsFile(mesh_path)) {
515  std::cerr << "ERROR: `mesh_path` does not exist" << std::endl;
516  return EXIT_FAILURE;
517  }
518 
519  CreateDirIfNotExists(output_path);
520 
521  PrintHeading1("Reading reconstruction");
522  Reconstruction reconstruction;
523  reconstruction.Read(JoinPaths(input_path, "sparse"));
524  std::cout << StringPrintf(" => Reconstruction with %d images and %d points",
525  reconstruction.NumImages(),
526  reconstruction.NumPoints3D())
527  << std::endl;
528 
529  options.texturing->meshed_file_path = mesh_path;
530  options.texturing->textured_file_path =
531  JoinPaths(output_path, "textured_mesh.obj");
532 
533  TexturingReconstruction texturer(*options.texturing, reconstruction,
534  *options.image_path, input_path);
535  texturer.Start();
536  texturer.Wait();
537 
538  return EXIT_SUCCESS;
539 }
540 } // namespace colmap
std::shared_ptr< core::Tensor > image
void SetWidth(const size_t width)
Definition: camera.h:164
bool VerifyParams() const
Definition: camera.cc:182
void SetModelIdFromName(const std::string &model_name)
Definition: camera.cc:57
const std::vector< double > & Params() const
Definition: camera.h:176
void SetHeight(const size_t height)
Definition: camera.h:166
void Load(const Database &database, const size_t min_num_matches, const bool ignore_watermarks, const std::unordered_set< std::string > &image_names)
void EndReconstruction(const bool discard)
void BeginReconstruction(Reconstruction *reconstruction)
bool RegisterNextImage(const Options &options, const image_t image_id)
void AddRequiredOption(const std::string &name, T *option, const std::string &help_text="")
std::shared_ptr< TexturingOptions > texturing
void AddDefaultOption(const std::string &name, T *option, const std::string &help_text="")
std::shared_ptr< std::string > database_path
std::shared_ptr< IncrementalMapperOptions > mapper
void Parse(const int argc, char **argv)
std::shared_ptr< std::string > image_path
size_t NumImages() const
size_t NumRegImages() const
void Write(const std::string &path) const
const std::unordered_map< image_t, class Image > & Images() const
std::vector< image_t > FilterImages(const double min_focal_length_ratio, const double max_focal_length_ratio, const double max_extra_param)
const class Image & Image(const image_t image_id) const
size_t NumPoints3D() const
const class Image * FindImageWithName(const std::string &name) const
void DeRegisterImage(const image_t image_id)
bool ExistsImage(const image_t image_id) const
void Read(const std::string &path)
virtual void Start()
Definition: threading.cc:50
virtual void Wait()
Definition: threading.cc:86
void Start()
Definition: timer.cc:43
void PrintMinutes() const
Definition: timer.cc:93
QTextStream & endl(QTextStream &stream)
Definition: QtCompat.h:718
static const std::string path
Definition: PointCloud.cpp:59
CopyType
Definition: misc.h:27
int RunImageUndistorter(int argc, char **argv)
Definition: image.cc:311
int RunImageFilterer(int argc, char **argv)
Definition: image.cc:156
int RunImageUndistorterStandalone(int argc, char **argv)
Definition: image.cc:408
void StringTrim(std::string *str)
Definition: string.cc:188
bool ExistsCameraModelWithName(const std::string &model_name)
int RunImageRectifier(int argc, char **argv)
Definition: image.cc:205
void StringToLower(std::string *str)
Definition: string.cc:193
int RunImageDeleter(int argc, char **argv)
Definition: image.cc:78
int RunImageTexturer(int argc, char **argv)
Definition: image.cc:497
std::vector< std::string > ReadTextFileLines(const std::string &path)
Definition: misc.cc:308
bool ExistsDir(const std::string &path)
Definition: misc.cc:104
bool ExistsFile(const std::string &path)
Definition: misc.cc:100
int RunImageRegistrator(int argc, char **argv)
Definition: image.cc:240
void CreateDirIfNotExists(const std::string &path)
Definition: misc.cc:112
std::string JoinPaths(T const &... paths)
Definition: misc.h:128
uint32_t image_t
Definition: types.h:61
void PrintHeading1(const std::string &heading)
Definition: misc.cc:225
std::vector< std::string > StringSplit(const std::string &str, const std::string &delim)
Definition: string.cc:166
std::string StringPrintf(const char *format,...)
Definition: string.cc:131
std::string to_string(const T &n)
Definition: Common.h:20