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 << std::endl;
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 << " File type: " << info.getFileType() << std::endl;
129  const auto fileSize = std::filesystem::file_size(file);
130  std::cout << " File size: ";
131  if (fileSize > 1000)
132  {
133  std::cout << fileSize / 1000 << " kB" << std::endl;
134  }
135  else
136  {
137  std::cout << fileSize << " B" << std::endl;
138  };
139  std::cout << " Pixel type: " << info.getPixelType() << std::endl
140  << " Pixel size: " << info.width() << "x" << info.height() << std::endl
141  << " Resolution: " << info.getXResolution() << "/" << info.getYResolution() << std::endl
142  << " Offset: " << info.getPosition() << std::endl
143  << " Canvas size: " << info.getCanvasSize() << std::endl
144  << " ICC profile: " << (info.getICCProfile().empty() ? "no" : hugin_utils::GetICCDesc(info.getICCProfile())) << std::endl
145  << " Bands: " << info.numBands() << std::endl
146  << " Extra bands: " << info.numExtraBands() << std::endl;
147  if (info.numImages() > 1)
148  {
149  std::cout << " Subimages: " << info.numImages() << " (reading index " << info.getImageIndex() << ")" << std::endl;
150  };
151  if (info.numExtraBands() == 0)
152  {
153  // no mask
154  if (info.numBands() == 3)
155  {
156  //RGB image
157  vigra::FRGBImage image(info.size());
158  vigra::importImage(info, destImage(image));
159  InspectRGBImage(image);
160  }
161  else
162  {
163  if (info.numBands() == 1)
164  {
165  // grayscale image
166  vigra::FImage image(info.size());
167  vigra::importImage(info, destImage(image));
168  InspectGrayscaleImage(image, "Grey channel");
169  }
170  else
171  {
172  std::cout << " Only RGB or grayscale images supported" << std::endl;
173  };
174  };
175  std::cout << std::endl;
176  }
177  else
178  {
179  if (info.numExtraBands() == 1)
180  {
181  // single mask
182  if (info.numBands() == 4)
183  {
184  // RGB image with mask
185  vigra::FRGBImage image(info.size());
186  vigra::FImage mask(info.size());
188  InspectRGBImage(image);
189  InspectGrayscaleImage(mask, "Mask");
190  }
191  else
192  {
193  if (info.numBands() == 2)
194  {
195  // grayscale image with mask
196  vigra::FImage image(info.size());
197  vigra::FImage mask(info.size());
199  InspectGrayscaleImage(image, "Grey channel");
200  InspectGrayscaleImage(mask, "Mask");
201  }
202  else
203  {
204  std::cout << " Only RGB or grayscale images supported" << std::endl;
205  };
206  };
207  std::cout << std::endl;
208  }
209  else
210  {
211  std::cout << " Only images with one or no alpha channel supported" << std::endl;
212  };
213  };
214  }
215  else
216  {
217  // no recognized image type
218  std::filesystem::path file(filename);
219  std::cout << std::endl << " not recognized by vigra as image file" << std::endl
220  << " File size: " << std::filesystem::file_size(file) / 1024 << " kiB" << std::endl << std::endl;
221  };
222  }
223  else
224  {
225  // file does not exists
226  std::cout << " does not exists." << std::endl <<std::endl;
227  };
228  };
229 }
230 
231 void CreateMissingImages(HuginBase::Panorama& pano, const std::string& output)
232 {
233  // init the random generator
234  std::mt19937 rng(0);
235  std::uniform_int_distribution<> distribIndex(0, 128);
236  auto randIndex = std::bind(distribIndex, rng);
237 
238  bool requiresPTOrewrite = false;
239  // store path to pto file for easier access
240  const std::string ptoPath = hugin_utils::getPathPrefix(hugin_utils::GetAbsoluteFilename(output));
241  const std::filesystem::path imagePath(ptoPath);
242  // check all images
243  for (int imgNr = 0; imgNr < pano.getNrOfImages(); ++imgNr)
244  {
245  std::filesystem::path srcFile(pano.getImage(imgNr).getFilename());
246  std::cout << "Image " << imgNr << ": " << srcFile.string();
247  if (std::filesystem::exists(srcFile))
248  {
249  std::cout << " exists." << std::endl;
250  }
251  else
252  {
253  // image does not exists
254  srcFile = std::filesystem::absolute(srcFile);
255  std::filesystem::path newImage(imagePath);
256  newImage /= srcFile.filename();
257  // file is not in the same directory as pto file, adjust path
258  if (newImage != srcFile)
259  {
260  requiresPTOrewrite = true;
261  pano.setImageFilename(imgNr, newImage.string());
262  };
263  std::cout << " does not exist. Creating " << newImage.filename() << std::endl;
264  // now create dummy image and save it to disc
265  vigra::UInt8RGBImage image(pano.getImage(imgNr).getWidth(), pano.getImage(imgNr).getHeight(),
266  vigra::RGBValue<vigra::UInt8>(64 + randIndex(), 64 + randIndex(), 64 + randIndex()));
267  vigra::ImageExportInfo imgExportInfo(newImage.string().c_str());
268  vigra::exportImage(vigra::srcImageRange(image), imgExportInfo);
269  };
270  };
271  if (requiresPTOrewrite)
272  {
273  // resave pto file in case path to some images has changed
274  if (pano.WritePTOFile(output, ptoPath))
275  {
276  std::cout << "Written output to " << output << std::endl;
277  };
278  };
279 }
280 
281 int main(int argc, char* argv[])
282 {
283  // parse arguments
284  const char* optstring = "h";
285  enum
286  {
287  PRINT_OUTPUT_INFO=1000,
288  PRINT_LENS_INFO=1004,
289  PRINT_STACK_INFO=1005,
290  CREATE_DUMMY_IMAGES=1006,
291  PRINT_IMAGE_INFO,
292  };
293  static struct option longOptions[] =
294  {
295  { "print-output-info", no_argument, NULL, PRINT_OUTPUT_INFO },
296  { "print-lens-info", no_argument, NULL, PRINT_LENS_INFO },
297  { "print-stack-info", no_argument, NULL, PRINT_STACK_INFO },
298  { "print-image-info", no_argument, NULL, PRINT_IMAGE_INFO },
299  { "create-missing-images", no_argument, NULL, CREATE_DUMMY_IMAGES },
300  { "help", no_argument, NULL, 'h' },
301  0
302  };
303 
304  int c;
305  bool printOutputInfo=false;
306  bool printLensInfo = false;
307  bool printStackInfo = false;
308  bool printImageInfo = false;
309  bool createDummyImages = false;
310  int optionIndex = 0;
311  while ((c = getopt_long (argc, argv, optstring, longOptions,nullptr)) != -1)
312  {
313  switch (c)
314  {
315  case 'h':
316  usage(hugin_utils::stripPath(argv[0]).c_str());
317  return 0;
318  case PRINT_OUTPUT_INFO:
319  printOutputInfo=true;
320  break;
321  case PRINT_LENS_INFO:
322  printLensInfo = true;
323  break;
324  case PRINT_STACK_INFO:
325  printStackInfo = true;
326  break;
327  case PRINT_IMAGE_INFO:
328  printImageInfo = true;
329  break;
330  case CREATE_DUMMY_IMAGES:
331  createDummyImages = true;
332  break;
333  case '?':
334  case ':':
335  // missing argument or invalid switch
336  return 1;
337  break;
338  default:
339  // this should not happen
340  abort ();
341  }
342  }
343 
344  if (argc - optind != 1)
345  {
346  if (argc - optind < 1)
347  {
348  std::cerr << hugin_utils::stripPath(argv[0]) << ": No project file given." << std::endl;
349  }
350  else
351  {
352  std::cerr << hugin_utils::stripPath(argv[0]) << ": Only one project file expected." << std::endl;
353  };
354  return -1;
355  }
356 
357 
358  std::string input=argv[optind];
359  // filename for output if needed
360  std::string output;
361  if (createDummyImages)
362  {
363  output = hugin_utils::GetOutputFilename(output, input, "dummy");
364  };
365 
366  HuginBase::Panorama pano;
367  if (!pano.ReadPTOFile(input, hugin_utils::getPathPrefix(input)))
368  {
369  return -1;
370  };
371 
372  HuginBase::ConstStandardImageVariableGroups variable_groups(pano);
373  std::cout << std::endl
374  << "Opened project " << input << std::endl << std::endl
375  << "Project contains" << std::endl
376  << pano.getNrOfImages() << " images" << std::endl
377  << variable_groups.getLenses().getNumberOfParts() << " lenses" << std::endl
378  << variable_groups.getStacks().getNumberOfParts() << " stacks" << std::endl
379  << pano.getNrOfCtrlPoints() << " control points" << std::endl << std::endl;
380  //cp statistics
381  if(pano.getNrOfCtrlPoints()>0)
382  {
383  double min;
384  double max;
385  double mean;
386  double var;
389  if(max>0)
390  {
391  std::ios oldstate(nullptr);
392  oldstate.copyfmt(std::cout);
393  std::cout << "Control points statistics" << std::endl
394  << std::fixed << std::setprecision(2)
395  << "\tMean error : " << mean << std::endl
396  << "\tStandard deviation: " << sqrt(var) << std::endl
397  << "\tMinimum : " << min << std::endl
398  << "\tMaximum : " << max << std::endl;
399  std::cout.copyfmt(oldstate);
400  };
401  };
402  HuginGraph::ImageGraph graph(pano);
404  int returnValue=0;
405  if(comps.size()==1)
406  {
407  std::cout << "All images are connected." << std::endl;
408  // return value must be 0, otherwise the assistant does not continue
409  returnValue=0;
410  }
411  else
412  {
413  std::cout << "Found unconnected images!" << std::endl
414  << "There are " << comps.size() << " image groups." << std::endl;
415 
416  std::cout << "Image groups: " << std::endl;
417  for (size_t i=0; i < comps.size(); i++)
418  {
419  std::cout << "[";
420  HuginGraph::ImageGraph::Components::value_type::const_iterator it;
421  size_t c=0;
422  for (it = comps[i].begin(); it != comps[i].end(); ++it)
423  {
424  std::cout << (*it);
425  if (c+1 != comps[i].size())
426  {
427  std::cout << ", ";
428  }
429  c++;
430  }
431  std::cout << "]";
432  if (i+1 != comps.size())
433  {
434  std::cout << ", " << std::endl;
435  }
436  }
437  returnValue=comps.size();
438  };
439  std::cout << std::endl;
440  if (printLensInfo)
441  {
442  std::cout << std::endl << "Lenses:" << std::endl;
443  PrintImageGroup(variable_groups.getLenses().getPartsSet(), "Lens");
444  };
445  if (printStackInfo)
446  {
447  std::cout << std::endl << "Stacks:" << std::endl;
448  PrintImageGroup(variable_groups.getStacks().getPartsSet(), "Stack");
449  };
450  if (printOutputInfo)
451  {
453  std::vector<HuginBase::UIntSet> stacks=HuginBase::getHDRStacks(pano, outputImages, pano.getOptions());
454  std::cout << std::endl << "Output contains" << std::endl
455  << stacks.size() << " images stacks:" << std::endl;
456  PrintImageGroup(stacks);
457  std::vector<HuginBase::UIntSet> layers=HuginBase::getExposureLayers(pano, outputImages, pano.getOptions());
458  std::cout << std::endl << std::endl << "and " << layers.size() << " exposure layers:" << std::endl;
459  PrintImageGroup(layers);
460  };
461  if (printImageInfo)
462  {
463  std::cout << "Supported file formats: " << vigra::impexListFormats() << std::endl
464  << "Supported extensions: " << vigra::impexListExtensions() << std::endl;
465  PrintImageInfo(pano);
466  };
467  if (createDummyImages)
468  {
469  CreateMissingImages(pano, output);
470  };
471  return returnValue;
472 }
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:231
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