Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
pto_gen.cpp
Go to the documentation of this file.
1 // -*- c-basic-offset: 4 -*-
2 
11 /* This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This software is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public
22  * License along with this software. If not, see
23  * <http://www.gnu.org/licenses/>.
24  *
25  */
26 
27 #include <fstream>
28 #include <getopt.h>
29 #ifdef _WIN32
30 #include <io.h>
31 #endif
32 #include <vigra/imageinfo.hxx>
33 #include <panodata/Panorama.h>
37 #include "hugin_utils/alphanum.h"
38 #include <lensdb/LensDB.h>
39 
40 #ifdef __APPLE__
41 #include <hugin_config.h>
42 #include <mach-o/dyld.h> /* _NSGetExecutablePath */
43 #include <limits.h> /* PATH_MAX */
44 #include <libgen.h> /* dirname */
45 #endif
46 
47 static void usage(const char* name)
48 {
49  std::cout << name << ": generate project file from images" << std::endl
50  << name << " version " << hugin_utils::GetHuginVersion() << std::endl
51  << std::endl
52  << "Usage: " << name << " [options] image1 [...]" << std::endl
53  << std::endl
54  << " Options:" << std::endl
55  << " -o, --output=file.pto Output Hugin PTO file." << std::endl
56  << " -p, --projection=INT Projection type (default: read from database)" << std::endl
57  << " -f, --fov=FLOAT Horizontal field of view of images" << std::endl
58  << " default: read from database" << std::endl
59  << " --ignore-fov-rectilinear Don't read fov for rectilinear images from" <<std::endl
60  << " the database, instead use only the values" << std::endl
61  << " from EXIF data" << std::endl
62  << " -c, --crop=left,right,top,bottom Sets the crop of input" << std::endl
63  << " images (especially for fisheye lenses)" << std::endl
64  << " -s, --stacklength=INT Number of images in stack" << std::endl
65  << " (default: automatic detection)" << std::endl
66  << " -l, --linkstacks Link image positions in stacks" << std::endl
67  << " --distortion Try to load distortion information from" << std::endl
68  << " lens database" << std::endl
69  << " --vignetting Try to load vignetting information from" << std::endl
70  << " lens database" << std::endl
71  << " --sort Sort the files by name alphanumeric" << std::endl
72  << " otherwise the images are processed in the order" << std::endl
73  << " given at the command line" << std::endl
74  << " -h, --help Shows this help" << std::endl
75  << std::endl;
76 }
77 
78 int main(int argc, char* argv[])
79 {
80  // parse arguments
81  const char* optstring = "o:p:f:c:s:lh";
82 
83  enum {
84  SWITCH_IGNORE_FOV_RECTILINEAR = 1000,
85  SWITCH_DISTORTION,
86  SWITCH_VIGNETTING,
87  SWITCH_SORT_FILENAME
88  };
89  static struct option longOptions[] =
90  {
91  {"output", required_argument, NULL, 'o' },
92  {"projection", required_argument, NULL, 'p' },
93  {"fov", required_argument, NULL, 'f' },
94  {"ignore-fov-rectilinear", no_argument, NULL, SWITCH_IGNORE_FOV_RECTILINEAR},
95  {"crop", required_argument, NULL, 'c' },
96  {"stacklength", required_argument, NULL, 's' },
97  {"linkstacks", no_argument, NULL, 'l' },
98  {"distortion", no_argument, NULL, SWITCH_DISTORTION },
99  {"vignetting", no_argument, NULL, SWITCH_VIGNETTING },
100  {"sort", no_argument, NULL, SWITCH_SORT_FILENAME },
101  {"help", no_argument, NULL, 'h' },
102 
103  0
104  };
105 
106  int c;
107  std::string output;
108  int projection=-1;
109  float fov=-1;
110  int stackLength=0;
111  bool linkStacks=false;
112  vigra::Rect2D cropRect(0,0,0,0);
113  bool ignoreFovRectilinear = false;
114  bool loadDistortion=false;
115  bool loadVignetting=false;
116  bool sortByFilename = false;
117  while ((c = getopt_long (argc, argv, optstring, longOptions,nullptr)) != -1)
118  {
119  switch (c)
120  {
121  case 'o':
122  output = optarg;
123  break;
124  case 'h':
125  usage(hugin_utils::stripPath(argv[0]).c_str());
126  return 0;
127  case 'p':
128  {
129  projection=atoi(optarg);
130  if((projection==0) && (strcmp(optarg,"0")!=0))
131  {
132  std::cerr << hugin_utils::stripPath(argv[0]) << ": Could not parse image number." << std::endl;
133  return 1;
134  };
135  if(projection<0)
136  {
137  std::cerr << hugin_utils::stripPath(argv[0]) << ": Invalid projection number." << std::endl;
138  return 1;
139  };
140  };
141  break;
142  case 'f':
143  fov=atof(optarg);
144  // normally fov should be <=360
145  // but for some multi-lens cameras/one shot panoramic cameras these images can also
146  // have a fov>360 because the fisheye projections covers only a small portion of the
147  // the whole image
148  if(fov<1 || fov>500)
149  {
150  std::cerr << hugin_utils::stripPath(argv[0]) << ": Invalid field of view" << std::endl;
151  return 1;
152  };
153  break;
154  case 'c':
155  {
156  int left, right, top, bottom;
157  int n=sscanf(optarg, "%d,%d,%d,%d", &left, &right, &top, &bottom);
158  if (n==4)
159  {
160  if(right>left && bottom>top)
161  {
162  cropRect.setUpperLeft(vigra::Point2D(left,top));
163  cropRect.setLowerRight(vigra::Point2D(right,bottom));
164  }
165  else
166  {
167  std::cerr << hugin_utils::stripPath(argv[0]) << ": Invalid crop area" << std::endl;
168  return 1;
169  };
170  }
171  else
172  {
173  std::cerr << hugin_utils::stripPath(argv[0]) << ": Could not parse crop values" << std::endl;
174  return 1;
175  };
176  };
177  break;
178  case 's':
179  stackLength=atoi(optarg);
180  if ((stackLength == 0) && (strcmp(optarg, "0") != 0))
181  {
182  std::cerr << hugin_utils::stripPath(argv[0]) << ": Could not parse stack length." << std::endl;
183  return 1;
184  };
185  break;
186  case 'l':
187  linkStacks=true;
188  break;
189  case SWITCH_DISTORTION:
190  loadDistortion=true;
191  break;
192  case SWITCH_VIGNETTING:
193  loadVignetting=true;
194  break;
195  case SWITCH_IGNORE_FOV_RECTILINEAR:
196  ignoreFovRectilinear = true;
197  break;
198  case SWITCH_SORT_FILENAME:
199  sortByFilename = true;
200  break;
201  case ':':
202  case '?':
203  // missing argument or invalid switch
204  return 1;
205  break;
206  default:
207  // this should not happen
208  abort ();
209  }
210  }
211 
212  if (argc - optind < 1)
213  {
214  std::cerr << hugin_utils::stripPath(argv[0]) << ": No image files given." << std::endl;
215  return 1;
216  };
217 
218  std::cout << "Generating pto file..." << std::endl;
219  std::cout.flush();
220 
221  std::vector<std::string> filelist;
222  while(optind<argc)
223  {
224  std::string input;
225 #ifdef _WIN32
226  //do globbing
227  input = hugin_utils::GetAbsoluteFilename(argv[optind]);
228  char drive[_MAX_DRIVE];
229  char dir[_MAX_DIR];
230  _splitpath(input.c_str(), drive, dir, NULL, NULL);
231 
232  struct _finddata_t finddata;
233  intptr_t findhandle = _findfirst(input.c_str(), &finddata);
234  if (findhandle != -1)
235  {
236  do
237  {
238  //ignore folder, can be happen when using *.*
239  if((finddata.attrib & _A_SUBDIR)==0)
240  {
241  char fname[_MAX_FNAME];
242  char ext[_MAX_EXT];
243  char newFile[_MAX_PATH];
244  _splitpath(finddata.name, NULL, NULL, fname, ext);
245  _makepath(newFile, drive, dir, fname, ext);
246  //check if valid image file
247  if(vigra::isImage(newFile))
248  {
249  filelist.push_back(std::string(newFile));
250  };
251  };
252  }
253  while (_findnext(findhandle, &finddata) == 0);
254  _findclose(findhandle);
255  }
256 #else
257  input=argv[optind];
258  if(hugin_utils::FileExists(input))
259  {
260  if(vigra::isImage(input.c_str()))
261  {
262  filelist.push_back(hugin_utils::GetAbsoluteFilename(input));
263  };
264  };
265 #endif
266  optind++;
267  };
268 
269  if(filelist.empty())
270  {
271  std::cerr << hugin_utils::stripPath(argv[0]) << ": No valid image files given." << std::endl;
272  return 1;
273  };
274 
275  //sort filenames if requested
276  if (sortByFilename)
277  {
278  sort(filelist.begin(), filelist.end(), doj::alphanum_less());
279  };
280 
281  HuginBase::Panorama pano;
282  double redBalanceAnchor = 1.0;
283  double blueBalanceAnchor = 1.0;
284  for(size_t i=0; i<filelist.size(); i++)
285  {
286  const std::string ext = hugin_utils::getExtension(filelist[i]);
287  // check that extension does not belong to a raw file
289  {
290  std::cerr << "Ignoring raw file " << filelist[i] << "." << std::endl;
291  continue;
292  };
294  std::cout << "Reading " << filelist[i] << "..." << std::endl;
295  srcImage.setFilename(filelist[i]);
296  try
297  {
298  vigra::ImageImportInfo info(filelist[i].c_str());
299  if(info.width()==0 || info.height()==0)
300  {
301  std::cerr << "ERROR: Could not decode image " << filelist[i] << std::endl
302  << "Skipping this image." << std::endl << std::endl;
303  continue;
304  }
305  srcImage.setSize(info.size());
306  // check for black/white images
307  const std::string pixelType=info.getPixelType();
308  if (pixelType == "BILEVEL")
309  {
310  std::cerr << "ERROR: Image " << filelist[i] << " is a black/white images." << std::endl
311  << " This is not supported. Convert to grayscale image and try again." << std::endl
312  << " Skipping this image." << std::endl;
313  continue;
314  }
315  if((pixelType=="UINT8") || (pixelType=="UINT16") || (pixelType=="INT16"))
316  {
317  srcImage.setResponseType(HuginBase::SrcPanoImage::RESPONSE_EMOR);
318  }
319  else
320  {
321  srcImage.setResponseType(HuginBase::SrcPanoImage::RESPONSE_LINEAR);
322  };
323  }
324  catch(std::exception& e)
325  {
326  std::cerr << "ERROR: caught exception: " << e.what() << std::endl;
327  std::cerr << "Could not read image information for file " << filelist[i] << std::endl;
328  std::cerr << "Skipping this image." << std::endl << std::endl;
329  continue;
330  };
331 
332  srcImage.readEXIF();
333  bool fovOk=srcImage.applyEXIFValues();
334  if(projection>=0)
335  {
336  srcImage.setProjection((HuginBase::BaseSrcPanoImage::Projection)projection);
337  }
338  else
339  {
340  srcImage.readProjectionFromDB(ignoreFovRectilinear);
341  };
342  if(fov>0)
343  {
344  srcImage.setHFOV(fov);
345  if(srcImage.getCropFactor()==0)
346  {
347  srcImage.setCropFactor(1.0);
348  };
349  }
350  else
351  {
352  //set plausible default value if they could not read from exif
353  if(!fovOk)
354  {
355  std::cout << "\tNo value for field of view found in EXIF data. " << std::endl
356  << "\tAssuming a HFOV of 50 degrees. " << std::endl;
357  srcImage.setHFOV(50);
358  srcImage.setCropFactor(1.0);
359  };
360  };
361  if(cropRect.width()>0 && cropRect.height()>0)
362  {
363  if(srcImage.isCircularCrop())
364  {
366  }
367  else
368  {
370  };
371  srcImage.setAutoCenterCrop(false);
372  srcImage.setCropRect(cropRect);
373  };
374  if(loadDistortion)
375  {
376  if(srcImage.readDistortionFromDB())
377  {
378  std::cout << "\tRead distortion data from lens database." << std::endl;
379  }
380  else
381  {
382  std::cout << "\tNo valid distortion data found in lens database." << std::endl;
383  };
384  };
385  if(loadVignetting)
386  {
387  if(srcImage.readVignettingFromDB())
388  {
389  std::cout << "\tRead vignetting data from lens database." << std::endl;
390  }
391  else
392  {
393  std::cout << "\tNo valid vignetting data found in lens database." << std::endl;
394  };
395  };
396  if (pano.getNrOfImages() == 0)
397  {
398  redBalanceAnchor = srcImage.getExifRedBalance();
399  blueBalanceAnchor = srcImage.getExifBlueBalance();
400  if (fabs(redBalanceAnchor)<1e-2)
401  {
402  redBalanceAnchor = 1;
403  };
404  if (fabs(blueBalanceAnchor)<1e-2)
405  {
406  blueBalanceAnchor = 1;
407  };
408  }
409  else
410  {
411  srcImage.setWhiteBalanceRed(srcImage.getExifRedBalance() / redBalanceAnchor);
412  srcImage.setWhiteBalanceBlue(srcImage.getExifBlueBalance() / blueBalanceAnchor);
413  };
414  pano.addImage(srcImage);
415  };
416 
417  if(pano.getNrOfImages()==0)
418  {
419  std::cerr << "Adding images to project files failed." << std::endl;
421  return 1;
422  };
423 
424  //link lenses
425  if(pano.getNrOfImages()>1)
426  {
427  HuginBase::StandardImageVariableGroups variable_groups(pano);
428  HuginBase::ImageVariableGroup& lenses = variable_groups.getLenses();
429 
430  for(size_t i=1; i<pano.getNrOfImages(); i++)
431  {
432  int image=-1;
433  const HuginBase::SrcPanoImage& srcImg=pano.getImage(i);
434  for(size_t j=0; j<i; j++)
435  {
436  const HuginBase::SrcPanoImage& compareImg=pano.getImage(j);
437  if(srcImg.getHFOV()==compareImg.getHFOV() &&
438  srcImg.getProjection()==compareImg.getProjection() &&
439  srcImg.getExifModel()==compareImg.getExifModel() &&
440  srcImg.getExifMake()==compareImg.getExifMake() &&
441  srcImg.getSize()==compareImg.getSize())
442  {
443  image=j;
444  break;
445  };
446  };
447  if(image!=-1)
448  {
449  lenses.switchParts(i,lenses.getPartNumber(image));
450  };
451  };
452  std::cout << std::endl << "Assigned " << lenses.getNumberOfParts() << " lenses." << std::endl;
453  if(lenses.getNumberOfParts()>1 && stackLength!=1)
454  {
455  std::cout << "Project contains more than one lens, but you requested to assign" << std::endl
456  << "stacks. This is not supported. Therefore stacks will not be" << std::endl
457  << "assigned." << std::endl << std::endl;
458  stackLength=1;
459  };
460 
461  if (stackLength == 0)
462  {
463  // automatic detection
464  if (pano.hasPossibleStacks())
465  {
466  pano.linkPossibleStacks(linkStacks);
467  };
468  }
469  else
470  {
471  if (stackLength > 1)
472  {
473  stackLength = std::min<int>(stackLength, pano.getNrOfImages());
474  int stackCount = pano.getNrOfImages() / stackLength;
475  if (pano.getNrOfImages() % stackLength > 0)
476  {
477  stackCount++;
478  };
479  if (stackCount < pano.getNrOfImages())
480  {
481  for (size_t stackNr = 0; stackNr < stackCount; stackNr++)
482  {
483  size_t firstImgStack = stackNr*stackLength;
484  for (size_t i = 0; i < stackLength; i++)
485  {
486  if (firstImgStack + i < pano.getNrOfImages())
487  {
488  pano.linkImageVariableStack(firstImgStack, firstImgStack + i);
489  if (linkStacks)
490  {
491  pano.linkImageVariableYaw(firstImgStack, firstImgStack + i);
492  pano.linkImageVariablePitch(firstImgStack, firstImgStack + i);
493  pano.linkImageVariableRoll(firstImgStack, firstImgStack + i);
494  pano.linkImageVariableX(firstImgStack, firstImgStack + i);
495  pano.linkImageVariableY(firstImgStack, firstImgStack + i);
496  pano.linkImageVariableZ(firstImgStack, firstImgStack + i);
497  pano.linkImageVariableTranslationPlaneYaw(firstImgStack, firstImgStack + i);
498  pano.linkImageVariableTranslationPlanePitch(firstImgStack, firstImgStack + i);
499  };
500  };
501  };
502  };
503  };
504  };
505  };
506 
507  variable_groups.update();
508  const size_t stackCount = variable_groups.getStacks().getNumberOfParts();
509  if (stackCount != pano.getNrOfImages())
510  {
511  std::cout << "Assigned " << stackCount << " stacks: " << std::endl
512  << "\t" << (linkStacks ? "Linking position of images in stacks" : "Use individual positions of images in stacks") << std::endl;
513  };
514  };
515 
516  //set output exposure value
519  pano.setOptions(opt);
520  // set optimizer switches
523  pano.updateOptimizeVector();
524 
525  //output
526  if(output=="")
527  {
528  output=hugin_utils::stripExtension(pano.getImage(0).getFilename());
529  if(pano.getNrOfImages()>1)
530  {
531  output.append("-");
532  output.append(hugin_utils::stripExtension(hugin_utils::stripPath(pano.getImage(pano.getNrOfImages()-1).getFilename())));
533  };
534  output=output.append(".pto");
535  };
536  output = hugin_utils::GetAbsoluteFilename(output);
537  //write output
538  if (pano.WritePTOFile(output, hugin_utils::getPathPrefix(output)))
539  {
540  std::cout << std::endl << "Written output to " << output << std::endl;
541  };
542 
544  return 0;
545 }
void setCropMode(CropMode val)
Set the crop mode.
bool FileExists(const std::string &filename)
checks if file exists
Definition: utils.cpp:362
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...
unsigned int getPartNumber(unsigned int imageNr) const
Get a part number from an image number.
static double calcMeanExposure(const PanoramaData &pano)
bool isCircularCrop() const
returns true, if projection requires cicular crop
ImageVariableGroup & getStacks()
Get the ImageVariableGroup representing the group of stack variables.
void setPhotometricOptimizerSwitch(const int newSwitch)
sets the photometric optimizer master switch
Definition: Panorama.cpp:311
Somewhere to specify what variables belong to what.
Functor class to compare two objects with the &quot;Alphanum Algorithm&quot;.
Definition: alphanum.h:65
vigra::pair< typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::ImageConstAccessor > srcImage(const ROIImage< Image, Mask > &img)
Definition: ROIImage.h:300
void setOptimizerSwitch(const int newSwitch)
set optimizer master switch
Definition: Panorama.cpp:303
static void Clean()
cleanup the static LensDB instance, must be called at the end of the program
Definition: LensDB.cpp:2010
bool readProjectionFromDB(const bool ignoreFovRectilinear=true)
tries to read projection and crop area from lens database you need to call SrcPanoImage::readEXIF bef...
class to access Hugins camera and lens database
void linkPossibleStacks(bool linkPosition)
create automatically stacks as indicated by metadata
Definition: Panorama.cpp:2181
Model for a panorama.
Definition: Panorama.h:152
empirical model of response
Definition: SrcPanoImage.h:100
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
some definitions to work with optimizer master switches
std::size_t getNrOfImages() const
number of images.
Definition: Panorama.h:205
std::string getExtension(const std::string &basename2)
Get extension of a filename.
Definition: utils.cpp:99
ImageVariableGroup & getLenses()
Get the ImageVariableGroup representing the group of lens variables.
std::string stripExtension(const std::string &basename2)
remove extension of a filename
Definition: utils.cpp:130
unsigned int addImage(const SrcPanoImage &img)
the the number for a specific image
Definition: Panorama.cpp:319
Same as above, but use a non const panorama.
bool readVignettingFromDB()
tries to read vignetting data from lens database you need to call SrcPanoImage::readEXIF before to fi...
const PanoramaOptions & getOptions() const
returns the options for this panorama
Definition: Panorama.h:481
bool readDistortionFromDB()
tries to read distortion data from lens database you need to call SrcPanoImage::readEXIF before to fi...
static void usage()
Definition: Main.cpp:32
bool readEXIF()
try to fill out information about the image, by examining the exif data
void setSize(vigra::Size2D val)
Set the image size in pixels.
std::string GetHuginVersion()
return a string with version numbers
Definition: utils.cpp:907
void update()
Update part numbers for each variable group.
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
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
bool IsRawExtension(const std::string testExt)
return true if extension belongs to a raw file
Definition: utils.cpp:946
std::size_t getNumberOfParts() const
get the number of parts.
All variables of a source image.
Definition: SrcPanoImage.h:194
Panorama image options.
void switchParts(unsigned int ImageNr, unsigned int partNr)
switch a given image to a different part number.
const bool hasPossibleStacks() const
return true, if the metadata indicates that the projects is a bracketet project
Definition: Panorama.cpp:2137
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