Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
checkpto.cpp
Go to the documentation of this file.
1 // -*- c-basic-offset: 4 -*-
2 
12 /* This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public
14  * License as published by the Free Software Foundation; either
15  * version 2 of the License, or (at your option) any later version.
16  *
17  * This software is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20  * General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public
23  * License along with this software. If not, see
24  * <http://www.gnu.org/licenses/>.
25  *
26  */
27 
28 #include <hugin_config.h>
29 
30 #include <fstream>
31 #include <sstream>
32 #include <random>
33 #include <getopt.h>
34 
35 #include <panodata/Panorama.h>
41 #include <filesystem>
42 #include "vigra/impexalpha.hxx"
43 
44 static void usage(const char* name)
45 {
46  std::cout << name << ": report the number of image groups in a project" << std::endl
47  << name << " version " << hugin_utils::GetHuginVersion() << std::endl
48  << std::endl
49  << "Usage: " << name << " input.pto" << std::endl
50  << std::endl
51  << name << " examines the connections between images in a project and" << std::endl
52  << "reports back the number of parts or image groups in that project" << std::endl
53  << std::endl
54  << "Further switches:" << std::endl
55  << " --print-output-info Print more information about the output" << std::endl
56  << " --print-lens-info Print more information about lenses" << std::endl
57  << " --print-stack-info Print more information about assigned stacks" << std::endl
58  << " --print-image-info Print information about image files" << std::endl
59  << " --create-missing-images Creates placeholder images for non-existing" << std::endl
60  << " images in same directory as the pto file" << std::endl
61  << std::endl
62  << name << " is used by the assistant" << std::endl
63  << std::endl;
64 }
65 
66 void PrintImageGroup(const std::vector<HuginBase::UIntSet>& imageGroup, const std::string& prefix=std::string())
67 {
68  for (size_t i=0; i < imageGroup.size(); i++)
69  {
70  if (!prefix.empty())
71  {
72  std::cout << prefix << " " << i << ": ";
73  };
74  std::cout << "[";
75  size_t c=0;
76  for (HuginBase::UIntSet::const_iterator it = imageGroup[i].begin(); it != imageGroup[i].end(); ++it)
77  {
78  std::cout << (*it);
79  if (c+1 != imageGroup[i].size())
80  {
81  std::cout << ", ";
82  }
83  ++c;
84  }
85  std::cout << "]";
86  if (prefix.empty() && (i+1 != imageGroup.size()))
87  {
88  std::cout << ", ";
89  }
90  std::cout << std::endl;
91  }
92 };
93 
94 void InspectRGBImage(const vigra::FRGBImage &image)
95 {
96  vigra::FindMinMax<float> minmax;
97  vigra::inspectImage(srcImageRange(image, vigra::RedAccessor<vigra::RGBValue<float>>()), minmax);
98  std::cout << " Red channel: " << minmax.min << "-" << minmax.max << std::endl;
99  minmax.reset();
100  vigra::inspectImage(srcImageRange(image, vigra::GreenAccessor<vigra::RGBValue<float>>()), minmax);
101  std::cout << " Green channel: " << minmax.min << "-" << minmax.max << std::endl;
102  minmax.reset();
103  vigra::inspectImage(srcImageRange(image, vigra::BlueAccessor<vigra::RGBValue<float>>()), minmax);
104  std::cout << " Blue channel: " << minmax.min << "-" << minmax.max << std::endl;
105 }
106 
107 void InspectGrayscaleImage(const vigra::FImage &image, const std::string text)
108 {
109  vigra::FindMinMax<float> minmax;
110  vigra::inspectImage(srcImageRange(image), minmax);
111  std::cout << " " << text << ": " << minmax.min << "-" << minmax.max << std::endl;
112 }
113 
115 {
116  // print info for all images
117  std::cout << std::endl;
118  for (int imgNr = 0; imgNr < pano.getNrOfImages(); ++imgNr)
119  {
120  const std::string filename = pano.getImage(imgNr).getFilename();
121  std::cout << "Image " << imgNr << ": " << filename;
122  if (hugin_utils::FileExists(filename))
123  {
124  if (vigra::isImage(filename.c_str()))
125  {
126  vigra::ImageImportInfo info(filename.c_str());
127  std::filesystem::path file(filename);
128  std::cout << std::endl
129  << " File type: " << info.getFileType() << std::endl;
130  const auto fileSize = std::filesystem::file_size(file);
131  std::cout << " File size: ";
132  if (fileSize > 1000)
133  {
134  std::cout << fileSize / 1000 << " kB" << std::endl;
135  }
136  else
137  {
138  std::cout << fileSize << " B" << std::endl;
139  };
140  std::cout << " Pixel type: " << info.getPixelType() << std::endl
141  << " Pixel size: " << info.width() << "x" << info.height() << std::endl
142  << " Resolution: " << info.getXResolution() << "/" << info.getYResolution() << std::endl
143  << " Offset: " << info.getPosition() << std::endl
144  << " Canvas size: " << info.getCanvasSize() << std::endl
145  << " ICC profile: " << (info.getICCProfile().empty() ? "no" : hugin_utils::GetICCDesc(info.getICCProfile())) << std::endl
146  << " Bands: " << info.numBands() << std::endl
147  << " Extra bands: " << info.numExtraBands() << std::endl;
148  if (info.numImages() > 1)
149  {
150  std::cout << " Subimages: " << info.numImages() << " (reading index " << info.getImageIndex() << ")" << std::endl;
151  };
152  if (info.numExtraBands() == 0)
153  {
154  // no mask
155  if (info.numBands() == 3)
156  {
157  //RGB image
158  vigra::FRGBImage image(info.size());
159  vigra::importImage(info, destImage(image));
160  InspectRGBImage(image);
161  }
162  else
163  {
164  if (info.numBands() == 1)
165  {
166  // grayscale image
167  vigra::FImage image(info.size());
168  vigra::importImage(info, destImage(image));
169  InspectGrayscaleImage(image, "Grey channel");
170  }
171  else
172  {
173  std::cout << " Only RGB or grayscale images supported" << std::endl;
174  };
175  };
176  std::cout << std::endl;
177  }
178  else
179  {
180  if (info.numExtraBands() == 1)
181  {
182  // single mask
183  if (info.numBands() == 4)
184  {
185  // RGB image with mask
186  vigra::FRGBImage image(info.size());
187  vigra::FImage mask(info.size());
189  InspectRGBImage(image);
190  InspectGrayscaleImage(mask, "Mask");
191  }
192  else
193  {
194  if (info.numBands() == 2)
195  {
196  // grayscale image with mask
197  vigra::FImage image(info.size());
198  vigra::FImage mask(info.size());
200  InspectGrayscaleImage(image, "Grey channel");
201  InspectGrayscaleImage(mask, "Mask");
202  }
203  else
204  {
205  std::cout << " Only RGB or grayscale images supported" << std::endl;
206  };
207  };
208  std::cout << std::endl;
209  }
210  else
211  {
212  std::cout << " Only images with one or no alpha channel supported" << std::endl;
213  };
214  };
215  }
216  else
217  {
218  // no recognized image type
219  std::filesystem::path file(filename);
220  std::cout << std::endl << " not recognized by vigra as image file" << std::endl
221  << " File size: " << std::filesystem::file_size(file) / 1024 << " kiB" << std::endl << std::endl;
222  };
223  }
224  else
225  {
226  // file does not exists
227  std::cout << " does not exists." << std::endl <<std::endl;
228  };
229  };
230 }
231 
232 void CreateMissingImages(HuginBase::Panorama& pano, const std::string& output)
233 {
234  // init the random generator
235  std::mt19937 rng(0);
236  std::uniform_int_distribution<> distribIndex(0, 128);
237  auto randIndex = std::bind(distribIndex, rng);
238 
239  bool requiresPTOrewrite = false;
240  // store path to pto file for easier access
241  const std::string ptoPath = hugin_utils::getPathPrefix(hugin_utils::GetAbsoluteFilename(output));
242  const std::filesystem::path imagePath(ptoPath);
243  // check all images
244  for (int imgNr = 0; imgNr < pano.getNrOfImages(); ++imgNr)
245  {
246  std::filesystem::path srcFile(pano.getImage(imgNr).getFilename());
247  std::cout << "Image " << imgNr << ": " << srcFile.string();
248  if (std::filesystem::exists(srcFile))
249  {
250  std::cout << " exists." << std::endl;
251  }
252  else
253  {
254  // image does not exists
255  srcFile = std::filesystem::absolute(srcFile);
256  std::filesystem::path newImage(imagePath);
257  newImage /= srcFile.filename();
258  // file is not in the same directory as pto file, adjust path
259  if (newImage != srcFile)
260  {
261  requiresPTOrewrite = true;
262  pano.setImageFilename(imgNr, newImage.string());
263  };
264  std::cout << " does not exist. Creating " << newImage.filename() << std::endl;
265  // now create dummy image and save it to disc
266  vigra::UInt8RGBImage image(pano.getImage(imgNr).getWidth(), pano.getImage(imgNr).getHeight(),
267  vigra::RGBValue<vigra::UInt8>(64 + randIndex(), 64 + randIndex(), 64 + randIndex()));
268  vigra::ImageExportInfo imgExportInfo(newImage.string().c_str());
269  vigra::exportImage(vigra::srcImageRange(image), imgExportInfo);
270  };
271  };
272  if (requiresPTOrewrite)
273  {
274  // resave pto file in case path to some images has changed
275  if (pano.WritePTOFile(output, ptoPath))
276  {
277  std::cout << "Written output to " << output << std::endl;
278  };
279  };
280 }
281 
282 int main(int argc, char* argv[])
283 {
284  // parse arguments
285  const char* optstring = "h";
286  enum
287  {
288  PRINT_OUTPUT_INFO=1000,
289  PRINT_LENS_INFO=1004,
290  PRINT_STACK_INFO=1005,
291  CREATE_DUMMY_IMAGES=1006,
292  PRINT_IMAGE_INFO,
293  };
294  static struct option longOptions[] =
295  {
296  { "print-output-info", no_argument, NULL, PRINT_OUTPUT_INFO },
297  { "print-lens-info", no_argument, NULL, PRINT_LENS_INFO },
298  { "print-stack-info", no_argument, NULL, PRINT_STACK_INFO },
299  { "print-image-info", no_argument, NULL, PRINT_IMAGE_INFO },
300  { "create-missing-images", no_argument, NULL, CREATE_DUMMY_IMAGES },
301  { "help", no_argument, NULL, 'h' },
302  0
303  };
304 
305  int c;
306  bool printOutputInfo=false;
307  bool printLensInfo = false;
308  bool printStackInfo = false;
309  bool printImageInfo = false;
310  bool createDummyImages = false;
311  int optionIndex = 0;
312  while ((c = getopt_long (argc, argv, optstring, longOptions,nullptr)) != -1)
313  {
314  switch (c)
315  {
316  case 'h':
317  usage(hugin_utils::stripPath(argv[0]).c_str());
318  return 0;
319  case PRINT_OUTPUT_INFO:
320  printOutputInfo=true;
321  break;
322  case PRINT_LENS_INFO:
323  printLensInfo = true;
324  break;
325  case PRINT_STACK_INFO:
326  printStackInfo = true;
327  break;
328  case PRINT_IMAGE_INFO:
329  printImageInfo = true;
330  break;
331  case CREATE_DUMMY_IMAGES:
332  createDummyImages = true;
333  break;
334  case '?':
335  case ':':
336  // missing argument or invalid switch
337  return 1;
338  break;
339  default:
340  // this should not happen
341  abort ();
342  }
343  }
344 
345  if (argc - optind != 1)
346  {
347  if (argc - optind < 1)
348  {
349  std::cerr << hugin_utils::stripPath(argv[0]) << ": No project file given." << std::endl;
350  }
351  else
352  {
353  std::cerr << hugin_utils::stripPath(argv[0]) << ": Only one project file expected." << std::endl;
354  };
355  return -1;
356  }
357 
358 
359  std::string input=argv[optind];
360  // filename for output if needed
361  std::string output;
362  if (createDummyImages)
363  {
364  output = hugin_utils::GetOutputFilename(output, input, "dummy");
365  };
366 
367  HuginBase::Panorama pano;
368  if (!pano.ReadPTOFile(input, hugin_utils::getPathPrefix(input)))
369  {
370  return -1;
371  };
372 
373  HuginBase::ConstStandardImageVariableGroups variable_groups(pano);
374  std::cout << std::endl
375  << "Opened project " << input << std::endl << std::endl
376  << "Project contains" << std::endl
377  << pano.getNrOfImages() << " images" << std::endl
378  << variable_groups.getLenses().getNumberOfParts() << " lenses" << std::endl
379  << variable_groups.getStacks().getNumberOfParts() << " stacks" << std::endl
380  << pano.getNrOfCtrlPoints() << " control points" << std::endl << std::endl;
381  //cp statistics
382  if(pano.getNrOfCtrlPoints()>0)
383  {
384  double min;
385  double max;
386  double mean;
387  double var;
390  if(max>0)
391  {
392  std::cout << "Control points statistics" << std::endl
393  << std::fixed << std::setprecision(2)
394  << "\tMean error : " << mean << std::endl
395  << "\tStandard deviation: " << sqrt(var) << std::endl
396  << "\tMinimum : " << min << std::endl
397  << "\tMaximum : " << max << std::endl;
398  };
399  };
400  HuginGraph::ImageGraph graph(pano);
402  int returnValue=0;
403  if(comps.size()==1)
404  {
405  std::cout << "All images are connected." << std::endl;
406  // return value must be 0, otherwise the assistant does not continue
407  returnValue=0;
408  }
409  else
410  {
411  std::cout << "Found unconnected images!" << std::endl
412  << "There are " << comps.size() << " image groups." << std::endl;
413 
414  std::cout << "Image groups: " << std::endl;
415  for (size_t i=0; i < comps.size(); i++)
416  {
417  std::cout << "[";
418  HuginGraph::ImageGraph::Components::value_type::const_iterator it;
419  size_t c=0;
420  for (it = comps[i].begin(); it != comps[i].end(); ++it)
421  {
422  std::cout << (*it);
423  if (c+1 != comps[i].size())
424  {
425  std::cout << ", ";
426  }
427  c++;
428  }
429  std::cout << "]";
430  if (i+1 != comps.size())
431  {
432  std::cout << ", " << std::endl;
433  }
434  }
435  returnValue=comps.size();
436  };
437  std::cout << std::endl;
438  if (printLensInfo)
439  {
440  std::cout << std::endl << "Lenses:" << std::endl;
441  PrintImageGroup(variable_groups.getLenses().getPartsSet(), "Lens");
442  };
443  if (printStackInfo)
444  {
445  std::cout << std::endl << "Stacks:" << std::endl;
446  PrintImageGroup(variable_groups.getStacks().getPartsSet(), "Stack");
447  };
448  if (printOutputInfo)
449  {
451  std::vector<HuginBase::UIntSet> stacks=HuginBase::getHDRStacks(pano, outputImages, pano.getOptions());
452  std::cout << std::endl << "Output contains" << std::endl
453  << stacks.size() << " images stacks:" << std::endl;
454  PrintImageGroup(stacks);
455  std::vector<HuginBase::UIntSet> layers=HuginBase::getExposureLayers(pano, outputImages, pano.getOptions());
456  std::cout << std::endl << std::endl << "and " << layers.size() << " exposure layers:" << std::endl;
457  PrintImageGroup(layers);
458  };
459  if (printImageInfo)
460  {
461  std::cout << "Supported file formats: " << vigra::impexListFormats() << std::endl
462  << "Supported extensions: " << vigra::impexListExtensions() << std::endl;
463  PrintImageInfo(pano);
464  };
465  if (createDummyImages)
466  {
467  CreateMissingImages(pano, output);
468  };
469  return returnValue;
470 }
std::string GetICCDesc(const vigra::ImageImportInfo::ICCProfile &iccProfile)
returns description of given icc profile
Definition: utils.cpp:912
void InspectGrayscaleImage(const vigra::FImage &image, const std::string text)
Definition: checkpto.cpp:107
std::vector< UIntSet > getHDRStacks(const PanoramaData &pano, UIntSet allImgs, PanoramaOptions opts)
returns vector of set of output stacks
Definition: LayerStacks.cpp:35
bool FileExists(const std::string &filename)
checks if file exists
Definition: utils.cpp:362
declaration of functions to handle stacks and layers
std::vector< UIntSet > getExposureLayers(const PanoramaData &pano, UIntSet allImgs, PanoramaOptions opts)
returns vector of set of output exposure layers
Definition: LayerStacks.cpp:96
void InspectRGBImage(const vigra::FRGBImage &image)
Definition: checkpto.cpp:94
std::string GetOutputFilename(const std::string &out, const std::string &in, const std::string &suffix)
construct output filename, if ouput is known return this value otherwise use the input filename and a...
Definition: utils.cpp:420
ConstImageVariableGroup & getStacks()
Get the ImageVariableGroup representing the group of stack variables.
Somewhere to specify what variables belong to what.
static void calcCtrlPntsErrorStats(const PanoramaData &pano, double &min, double &max, double &mean, double &var, const int &imgNr=-1, const bool onlyActive=false, const bool ignoreLineCp=false)
UIntSet getImagesinROI(const PanoramaData &pano, const UIntSet activeImages)
returns set of images which are visible in output ROI
void PrintImageInfo(const HuginBase::Panorama &pano)
Definition: checkpto.cpp:114
std::size_t getNrOfCtrlPoints() const
number of control points
Definition: Panorama.h:306
int getHeight() const
Get the height of the image in pixels.
Definition: SrcPanoImage.h:276
std::vector< HuginBase::UIntSet > Components
stores the components of the graph
Definition: ImageGraph.h:50
void PrintImageGroup(const std::vector< HuginBase::UIntSet > &imageGroup, const std::string &prefix=std::string())
Definition: checkpto.cpp:66
void CreateMissingImages(HuginBase::Panorama &pano, const std::string &output)
Definition: checkpto.cpp:232
std::set< unsigned int > UIntSet
Definition: PanoramaData.h:51
void calcCtrlPointErrors(PanoramaData &pano)
Update the Ctrl Point errors without optimizing.
Model for a panorama.
Definition: Panorama.h:152
ConstImageVariableGroup & getLenses()
Get the ImageVariableGroup representing the group of lens variables.
UIntSetVector getPartsSet() const
return a vector which contains a HuginBase::UIntSet for each group with the corresponding images numb...
std::string getPathPrefix(const std::string &filename)
Get the path to a filename.
Definition: utils.cpp:184
std::string GetAbsoluteFilename(const std::string &filename)
returns the full absolute filename
Definition: utils.cpp:368
std::size_t getNrOfImages() const
number of images.
Definition: Panorama.h:205
int getWidth() const
Get the width of the image in pixels.
Definition: SrcPanoImage.h:266
bool ReadPTOFile(const std::string &filename, const std::string &prefix="")
read pto file from the given filename into Panorama object it does some checks on the file and issues...
Definition: Panorama.cpp:2023
Make an ImageVariableGroup for lenses and other common concepts.
void setImageFilename(unsigned int img, const std::string &fname)
set a new image filename
Definition: Panorama.cpp:373
vigra::pair< typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::ImageAccessor > destImage(ROIImage< Image, Alpha > &img)
Definition: ROIImage.h:324
vigra::triple< typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::ImageConstAccessor > srcImageRange(const ROIImage< Image, Mask > &img)
helper function for ROIImages
Definition: ROIImage.h:287
UIntSet getActiveImages() const
get active images
Definition: Panorama.cpp:1585
Utility calls into PanoTools using CPP interface.
void importImageAlpha(const ImageImportInfo &import_info, ImageIterator image_iterator, ImageAccessor image_accessor, AlphaIterator alpha_iterator, AlphaAccessor alpha_accessor)
Read the image specified by the given vigra::ImageImportInfo object including its alpha channel...
Definition: impexalpha.hxx:479
const PanoramaOptions & getOptions() const
returns the options for this panorama
Definition: Panorama.h:481
Components GetComponents()
find all connected components
Definition: ImageGraph.cpp:101
static T max(T x, T y)
Definition: svm.cpp:65
static void usage()
Definition: Main.cpp:32
std::string GetHuginVersion()
return a string with version numbers
Definition: utils.cpp:907
bool WritePTOFile(const std::string &filename, const std::string &prefix="")
write data to given pto file
Definition: Panorama.cpp:2059
static void info(const char *fmt,...)
Definition: svm.cpp:95
const SrcPanoImage & getImage(std::size_t nr) const
get a panorama image, counting starts with 0
Definition: Panorama.h:211
std::size_t getNumberOfParts() const
get the number of parts.
static T min(T x, T y)
Definition: svm.cpp:62
std::string stripPath(const std::string &filename)
remove the path of a filename (mainly useful for gui display of filenames)
Definition: utils.cpp:160
int main(int argc, char *argv[])
Definition: Main.cpp:167
class to work with images graphs created from a HuginBase::Panorama class it creates a graph based on...
Definition: ImageGraph.h:44