Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
autooptimiser.cpp
Go to the documentation of this file.
1 // -*- c-basic-offset: 4 -*-
2 
28 #include <hugin_config.h>
29 
30 #include <fstream>
31 #include <sstream>
32 #include <getopt.h>
33 
34 #include <hugin_basic.h>
35 #include <hugin_utils/stl_utils.h>
46 #include "ExtractPoints.h"
47 
48 static void usage(const char* name)
49 {
50  std::cout << name << ": optimize image positions" << std::endl
51  << "autooptimiser version " << hugin_utils::GetHuginVersion() << std::endl
52  << std::endl
53  << "Usage: " << name << " [options] input.pto" << std::endl
54  << " To read a project from stdio, specify - as input file." << std::endl
55  << std::endl
56  << " Options:" << std::endl
57  << " -o file.pto output file. If omitted, stdout is used." << std::endl
58  << std::endl
59  << " Optimisation options (if not specified, no optimisation takes place)" << std::endl
60  << " -a auto align mode, includes various optimisation stages, depending" << std::endl
61  << " on the amount and distribution of the control points" << std::endl
62  << " -p pairwise optimisation of yaw, pitch and roll, starting from" << std::endl
63  << " first image" << std::endl
64  << " -m Optimise photometric parameters" << std::endl
65  << " -n Optimize parameters specified in script file (like PTOptimizer)" << std::endl
66  << std::endl
67  << " Postprocessing options:" << std::endl
68  << " -l level horizon (works best for horizontal panos)" << std::endl
69  << " -s automatically select a suitable output projection and size" << std::endl
70  << " Other options:" << std::endl
71  << " -q quiet operation (no progress is reported)" << std::endl
72  << " -v HFOV specify horizontal field of view of input images." << std::endl
73  << " Used if the .pto file contains invalid HFOV values" << std::endl
74  << " (autopano-SIFT writes .pto files with invalid HFOV)" << std::endl
75  << std::endl
76  << " --only-active-images take only active images into account when" << std::endl
77  << " optimising (only valid with -n switch)" << std::endl
78  << std::endl
79  << " When using -a -l -m and -s options together, a similar operation to the" << std::endl
80  << " \"Align\" button in hugin is performed." << std::endl
81  << std::endl;
82 }
83 
84 int main(int argc, char* argv[])
85 {
86  // parse arguments
87  const char* optstring = "alho:npqsv:m";
88  int c;
89  enum
90  {
91  SWITCH_ONLY_ACTIVE=1000
92  };
93  static struct option longOptions[] =
94  {
95  { "output", required_argument, NULL, 'o'},
96  { "help", no_argument, NULL, 'h' },
97  { "only-active-images", no_argument, NULL, SWITCH_ONLY_ACTIVE},
98  0
99  };
100  std::string output;
101  bool doPairwise = false;
102  bool doAutoOpt = false;
103  bool doNormalOpt = false;
104  bool optOnlyActive = false;
105  bool doLevel = false;
106  bool chooseProj = false;
107  bool quiet = false;
108  bool doPhotometric = false;
109  double hfov = 0.0;
110  while ((c = getopt_long(argc, argv, optstring, longOptions, nullptr)) != -1)
111  {
112  switch (c)
113  {
114  case 'o':
115  output = optarg;
116  break;
117  case 'h':
118  usage(hugin_utils::stripPath(argv[0]).c_str());
119  return 0;
120  case 'p':
121  doPairwise = true;
122  break;
123  case 'a':
124  doAutoOpt = true;
125  break;
126  case 'n':
127  doNormalOpt = true;
128  break;
129  case 'l':
130  doLevel = true;
131  break;
132  case 's':
133  chooseProj = true;
134  break;
135  case 'q':
136  quiet = true;
137  break;
138  case 'v':
139  hfov = atof(optarg);
140  break;
141  case 'm':
142  doPhotometric = true;
143  break;
144  case SWITCH_ONLY_ACTIVE:
145  optOnlyActive = true;
146  break;
147  case ':':
148  case '?':
149  // missing argument or invalid switch
150  return 1;
151  break;
152  default:
153  // this should not happen
154  abort ();
155  }
156  }
157 
158  if (argc - optind != 1)
159  {
160  if (argc - optind < 1)
161  {
162  std::cerr << hugin_utils::stripPath(argv[0]) << ": No project file given." << std::endl;
163  }
164  else
165  {
166  std::cerr << hugin_utils::stripPath(argv[0]) << ": Only one project file expected." << std::endl;
167  };
168  return 1;
169  }
170 
171  const char* scriptFile = argv[optind];
172 
173  HuginBase::Panorama pano;
174  if (!pano.ReadPTOFile(scriptFile, hugin_utils::getPathPrefix(scriptFile)))
175  {
176  return 1;
177  };
178 
179  if (pano.getNrOfImages() == 0)
180  {
181  std::cerr << "Panorama should consist of at least one image" << std::endl;
182  return 1;
183  }
184 
185  // for bad HFOV (from autopano-SIFT)
186  for (unsigned i=0; i < pano.getNrOfImages(); i++)
187  {
188  HuginBase::SrcPanoImage img = pano.getSrcImage(i);
189  if (img.getProjection() == HuginBase::SrcPanoImage::RECTILINEAR
190  && img.getHFOV() >= 180)
191  {
192  // something is wrong here, try to read from exif data
193  std::cerr << "HFOV of image " << img.getFilename() << " invalid, trying to read EXIF tags" << std::endl;
194  img.readEXIF();
195  bool ok = img.applyEXIFValues(false);
196  if (! ok)
197  {
198  if (hfov)
199  {
200  img.setHFOV(hfov);
201  }
202  else
203  {
204  std::cerr << "EXIF reading failed, please specify HFOV with -v" << std::endl;
205  return 1;
206  }
207  }
208  pano.setSrcImage(i, img);
209  }
210  }
211  // update optimize vector, if switch is set to manual nothing is done
212  pano.updateOptimizeVector();
213 
214  if(pano.getNrOfCtrlPoints()==0 && (doPairwise || doAutoOpt || doNormalOpt))
215  {
216  std::cerr << "Panorama have to have control points to optimise positions" << std::endl;
217  return 1;
218  };
219  if (doPairwise && ! doAutoOpt)
220  {
221  // do pairwise optimisation
223 
224  // do global optimisation
225  if (!quiet)
226  {
227  std::cerr << "*** Pairwise position optimisation" << std::endl;
228  }
230  }
231  else if (doAutoOpt)
232  {
233  if (!quiet)
234  {
235  std::cerr << "*** Adaptive geometric optimisation" << std::endl;
236  }
238  }
239  else if (doNormalOpt)
240  {
241  if (optOnlyActive)
242  {
243  if (!quiet)
244  {
245  std::cerr << "*** Optimising parameters specified in PTO file (active images only)" << std::endl;
246  }
247  //optimise only active images
248  const HuginBase::UIntSet activeImages = pano.getActiveImages();
249  if (activeImages.empty())
250  {
251  std::cerr << "*** Image contains no active images. Nothing to do." << std::endl;
252  }
253  else
254  {
255  HuginBase::Panorama optPano = pano.getSubset(activeImages);
257  // write result back into initial pano
258  pano.updateVariables(activeImages, optPano.getVariables());
259  };
260  }
261  else
262  {
263  // optimise all images, independend of active state of individual images
264  if (!quiet)
265  {
266  std::cerr << "*** Optimising parameters specified in PTO file" << std::endl;
267  }
269  };
270  }
271  else
272  {
273  if (!quiet)
274  {
275  std::cerr << "*** Geometric parameters not optimized" << std::endl;
276  }
277  }
278 
279  if (doLevel)
280  {
281  bool hasVerticalLines=false;
282  HuginBase::CPVector allCP=pano.getCtrlPoints();
283  if(!allCP.empty() && (doPairwise || doAutoOpt || doNormalOpt))
284  {
285  for(size_t i=0; i<allCP.size() && !hasVerticalLines; i++)
286  {
287  hasVerticalLines=(allCP[i].mode==HuginBase::ControlPoint::X);
288  };
289  };
290  // straighten only if there are no vertical control points
291  if(hasVerticalLines)
292  {
293  std::cout << "Skipping automatic leveling because of existing vertical control points." << std::endl;
294  // center pano, ignore step for single image project files or when using rectilinear projection
295  // where vertical control points can be used for correction of perspective
297  {
299  }
300  }
301  else
302  {
305  };
306  }
307 
308  if (chooseProj)
309  {
311  HuginBase::CalculateFitPanorama fitPano(pano);
312  fitPano.run();
313  opts.setHFOV(fitPano.getResultHorizontalFOV());
315  const double vfov = opts.getVFOV();
316  const double hfov = opts.getHFOV();
317  // avoid perspective projection if field of view > 100 deg
318  const double mf = 100;
319  bool changedProjection=false;
320  if (pano.getNrOfImages() == 1)
321  {
322  // special case for single image projects
323  switch (pano.getImage(0).getProjection())
324  {
326  // single rectilinear image, keep rectilinear projection
328  {
330  changedProjection = true;
331  };
332  break;
333  default:
334  if (vfov < mf)
335  {
336  // small vfov, use cylindrical
338  {
340  changedProjection = true;
341  };
342  }
343  else
344  {
345  // otherwise go to equirectangular
347  {
349  changedProjection = true;
350  };
351  };
352  break;
353  };
354  }
355  else
356  {
357  if (vfov < mf)
358  {
359  // cylindrical or rectilinear
360  if (hfov < mf)
361  {
363  {
365  changedProjection = true;
366  };
367  }
368  else
369  {
371  {
373  changedProjection = true;
374  };
375  };
376  }
377  else
378  {
379  // vfov > 100, use equirectangular projection
381  {
383  changedProjection = true;
384  };
385  };
386  };
387  pano.setOptions(opts);
388  // the projection could be changed, calculate fit again
389  if(changedProjection)
390  {
391  HuginBase::CalculateFitPanorama fitPano2(pano);
392  fitPano2.run();
393  opts.setHFOV(fitPano2.getResultHorizontalFOV());
395  pano.setOptions(opts);
396  };
397 
398  // downscale pano a little
399  const double sizeFactor = 0.7;
401  opts.setWidth(hugin_utils::roundi(opts.getWidth()*w*sizeFactor), true);
402  pano.setOptions(opts);
403  }
404 
405  if(doPhotometric)
406  {
407  // photometric estimation
409  int nPoints = 200;
410  nPoints = nPoints * pano.getNrOfImages();
411 
412  std::vector<vigra_ext::PointPairRGB> points;
413  AppBase::ProgressDisplay* progressDisplay;
414  if(!quiet)
415  {
416  progressDisplay=new AppBase::StreamProgressDisplay(std::cout);
417  }
418  else
419  {
420  progressDisplay=new AppBase::DummyProgressDisplay();
421  }
422  float imageStepSize;
423  try
424  {
425  loadImgsAndExtractPoints(pano, nPoints, 3, true, *progressDisplay, points, !quiet, imageStepSize);
426  }
427  catch (std::exception& e)
428  {
429  std::cerr << "caught exception: " << e.what() << std::endl;
430  return 1;
431  };
432  if(!quiet)
433  {
434  std::cout << "\rSelected " << points.size() << " points" << std::endl;
435  }
436 
437  if (points.empty())
438  {
439  std::cerr << "Error: no overlapping points found, exiting" << std::endl;
440  return 1;
441  }
442 
443  progressDisplay->setMessage("Photometric Optimization");
444  // first, ensure that vignetting and response coefficients are linked
446  {
447  HuginBase::ImageVariableGroup::IVE_EMoRParams,
448  HuginBase::ImageVariableGroup::IVE_ResponseType,
449  HuginBase::ImageVariableGroup::IVE_VigCorrMode,
450  HuginBase::ImageVariableGroup::IVE_RadialVigCorrCoeff,
451  HuginBase::ImageVariableGroup::IVE_RadialVigCorrCenterShift
452  };
453  HuginBase::StandardImageVariableGroups variable_groups(pano);
454  HuginBase::ImageVariableGroup& lenses = variable_groups.getLenses();
455  for (size_t i = 0; i < lenses.getNumberOfParts(); i++)
456  {
457  std::set<HuginBase::ImageVariableGroup::ImageVariableEnum> links_needed;
458  links_needed.clear();
459  for (int v = 0; v < 5; v++)
460  {
461  if (!lenses.getVarLinkedInPart(vars[v], i))
462  {
463  links_needed.insert(vars[v]);
464  }
465  };
466  if (!links_needed.empty())
467  {
468  std::set<HuginBase::ImageVariableGroup::ImageVariableEnum>::iterator it;
469  for (it = links_needed.begin(); it != links_needed.end(); ++it)
470  {
471  lenses.linkVariablePart(*it, i);
472  }
473  }
474  }
475 
479  {
480  // use HDR algorithm is HDR mode is selected or the project contains stacks
482  }
483  HuginBase::SmartPhotometricOptimizer photoOpt(pano, progressDisplay, pano.getOptimizeVector(), points, imageStepSize, optmode);
484  photoOpt.run();
485 
486  // calculate the mean exposure.
488  pano.setOptions(opts);
489  progressDisplay->taskFinished();
490  delete progressDisplay;
491  };
492 
493  // write result
494  output = hugin_utils::GetOutputFilename(output, scriptFile, "opt");
495  if (pano.WritePTOFile(output, hugin_utils::getPathPrefix(output)))
496  {
497  std::cout << "Written output to " << output << std::endl;
498  };
499  return 0;
500 }
bool getVarLinkedInPart(ImageVariableEnum variable, std::size_t part) const
Get the linked status of a particular variable for a given part number.
PanoramaOptions::ProjectionFormat getProjection() const
Dummy progress display, without output.
void updateOptimizeVector()
updates the optimize vector according to master switches
Definition: Panorama.cpp:1299
bool applyEXIFValues(bool applyEVValue=true)
apply values found in EXIF data to SrcPanoImage class, call readEXIF() before to initialize some valu...
void setHeight(unsigned int h)
set panorama height
int roundi(T x)
Definition: hugin_math.h:73
static double calcMeanExposure(const PanoramaData &pano)
SrcPanoImage getSrcImage(unsigned imgNr) const
get a description of a source image
Definition: Panorama.cpp:1620
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
static void smartOptimize(PanoramaData &pano)
ImageVariableGroup & getStacks()
Get the ImageVariableGroup representing the group of stack variables.
Somewhere to specify what variables belong to what.
std::size_t getNrOfCtrlPoints() const
number of control points
Definition: Panorama.h:306
PhotometricOptimizeMode
local optimize definition.
virtual void run()
runs the algorithm.
void loadImgsAndExtractPoints(HuginBase::Panorama pano, int nPoints, int pyrLevel, bool randomPoints, AppBase::ProgressDisplay &progress, std::vector< vigra_ext::PointPairRGB > &points, int verbose, float &imageStepSize)
Panorama getSubset(const UIntSet &imgs) const
get a subset of the panorama
const CPVector & getCtrlPoints() const
get all control point of this Panorama
Definition: Panorama.h:319
virtual void updateVariables(const VariableMapVector &vars)
Set the variables.
Definition: Panorama.cpp:171
Declare the ImageVariableGroup and ImageVariableGroupObserver classes.
void linkVariablePart(ImageVariableEnum variable, unsigned int partNr)
link one of the variables across a given part
std::set< unsigned int > UIntSet
Definition: PanoramaData.h:51
Model for a panorama.
Definition: Panorama.h:152
void taskFinished()
call when a task has finished and the status message should be cleared
std::string getPathPrefix(const std::string &filename)
Get the path to a filename.
Definition: utils.cpp:184
const OptimizeVector & getOptimizeVector() const
return the optimize settings stored inside panorama
Definition: Panorama.h:454
VariableMapVector getVariables() const
get variables of this panorama
Definition: Panorama.cpp:118
std::size_t getNrOfImages() const
number of images.
Definition: Panorama.h:205
static double calcOptimalScale(PanoramaData &panorama)
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
virtual double getResultHeight()
Definition: FitPanorama.h:75
ImageVariableGroup & getLenses()
Get the ImageVariableGroup representing the group of lens variables.
void setMessage(const std::string &message, const std::string &filename="")
sets the message to given string
evaluate x, points are on a vertical line
Definition: ControlPoint.h:47
!! from PTOptimise.h 1951
UIntSet getActiveImages() const
get active images
Definition: Panorama.cpp:1585
Same as above, but use a non const panorama.
void setHFOV(double h, bool keepView=true)
set the horizontal field of view.
unsigned int getWidth() const
const PanoramaOptions & getOptions() const
returns the options for this panorama
Definition: Panorama.h:481
virtual double getResultHorizontalFOV()
Definition: FitPanorama.h:68
static void usage()
Definition: Main.cpp:32
bool readEXIF()
try to fill out information about the image, by examining the exif data
unsigned int optimize(PanoramaData &pano, const char *userScript)
optimize the images imgs, for variables optvec, using vars as start.
helper procedure for photometric optimizer on command line
std::string GetHuginVersion()
return a string with version numbers
Definition: utils.cpp:920
std::vector< ControlPoint > CPVector
Definition: ControlPoint.h:99
bool WritePTOFile(const std::string &filename, const std::string &prefix="")
write data to given pto file
Definition: Panorama.cpp:2059
const SrcPanoImage & getImage(std::size_t nr) const
get a panorama image, counting starts with 0
Definition: Panorama.h:211
void setOptions(const PanoramaOptions &opt)
set new output settings This is not used directly for optimizing/stiching, but it can be feed into ru...
Definition: Panorama.cpp:1531
void setSrcImage(unsigned int nr, const SrcPanoImage &img)
set input image parameters
std::size_t getNumberOfParts() const
get the number of parts.
All variables of a source image.
Definition: SrcPanoImage.h:194
void setProjection(ProjectionFormat f)
set the Projection format and adjust the hfov/vfov if nessecary
Panorama image options.
a progress display to print progress reports to a stream
std::string stripPath(const std::string &filename)
remove the path of a filename (mainly useful for gui display of filenames)
Definition: utils.cpp:160
void setWidth(unsigned int w, bool keepView=true)
set panorama width keep the HFOV, if keepView=true
static void autoOptimise(PanoramaData &pano, bool optRoll=true)
int main(int argc, char *argv[])
Definition: Main.cpp:167