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  if (hugin_utils::IsLinearICCProfile(info.getICCProfile()))
318  {
319  srcImage.setResponseType(HuginBase::SrcPanoImage::RESPONSE_LINEAR);
320  }
321  else
322  {
323  srcImage.setResponseType(HuginBase::SrcPanoImage::RESPONSE_EMOR);
324  }
325  }
326  else
327  {
328  srcImage.setResponseType(HuginBase::SrcPanoImage::RESPONSE_LINEAR);
329  };
330  }
331  catch(std::exception& e)
332  {
333  std::cerr << "ERROR: caught exception: " << e.what() << std::endl;
334  std::cerr << "Could not read image information for file " << filelist[i] << std::endl;
335  std::cerr << "Skipping this image." << std::endl << std::endl;
336  continue;
337  };
338 
339  srcImage.readEXIF();
340  bool fovOk=srcImage.applyEXIFValues();
341  if(projection>=0)
342  {
343  srcImage.setProjection((HuginBase::BaseSrcPanoImage::Projection)projection);
344  }
345  else
346  {
347  srcImage.readProjectionFromDB(ignoreFovRectilinear);
348  };
349  if(fov>0)
350  {
351  srcImage.setHFOV(fov);
352  if(srcImage.getCropFactor()==0)
353  {
354  srcImage.setCropFactor(1.0);
355  };
356  }
357  else
358  {
359  //set plausible default value if they could not read from exif
360  if(!fovOk)
361  {
362  std::cout << "\tNo value for field of view found in EXIF data. " << std::endl
363  << "\tAssuming a HFOV of 50 degrees. " << std::endl;
364  srcImage.setHFOV(50);
365  srcImage.setCropFactor(1.0);
366  };
367  };
368  if(cropRect.width()>0 && cropRect.height()>0)
369  {
370  if(srcImage.isCircularCrop())
371  {
373  }
374  else
375  {
377  };
378  srcImage.setAutoCenterCrop(false);
379  srcImage.setCropRect(cropRect);
380  };
381  if(loadDistortion)
382  {
383  if(srcImage.readDistortionFromDB())
384  {
385  std::cout << "\tRead distortion data from lens database." << std::endl;
386  }
387  else
388  {
389  std::cout << "\tNo valid distortion data found in lens database." << std::endl;
390  };
391  };
392  if(loadVignetting)
393  {
394  if(srcImage.readVignettingFromDB())
395  {
396  std::cout << "\tRead vignetting data from lens database." << std::endl;
397  }
398  else
399  {
400  std::cout << "\tNo valid vignetting data found in lens database." << std::endl;
401  };
402  };
403  if (pano.getNrOfImages() == 0)
404  {
405  redBalanceAnchor = srcImage.getExifRedBalance();
406  blueBalanceAnchor = srcImage.getExifBlueBalance();
407  if (fabs(redBalanceAnchor)<1e-2)
408  {
409  redBalanceAnchor = 1;
410  };
411  if (fabs(blueBalanceAnchor)<1e-2)
412  {
413  blueBalanceAnchor = 1;
414  };
415  }
416  else
417  {
418  srcImage.setWhiteBalanceRed(srcImage.getExifRedBalance() / redBalanceAnchor);
419  srcImage.setWhiteBalanceBlue(srcImage.getExifBlueBalance() / blueBalanceAnchor);
420  };
421  pano.addImage(srcImage);
422  };
423 
424  if(pano.getNrOfImages()==0)
425  {
426  std::cerr << "Adding images to project files failed." << std::endl;
428  return 1;
429  };
430 
431  //link lenses
432  if(pano.getNrOfImages()>1)
433  {
434  HuginBase::StandardImageVariableGroups variable_groups(pano);
435  HuginBase::ImageVariableGroup& lenses = variable_groups.getLenses();
436 
437  for(size_t i=1; i<pano.getNrOfImages(); i++)
438  {
439  int image=-1;
440  const HuginBase::SrcPanoImage& srcImg=pano.getImage(i);
441  for(size_t j=0; j<i; j++)
442  {
443  const HuginBase::SrcPanoImage& compareImg=pano.getImage(j);
444  if(srcImg.getHFOV()==compareImg.getHFOV() &&
445  srcImg.getProjection()==compareImg.getProjection() &&
446  srcImg.getExifModel()==compareImg.getExifModel() &&
447  srcImg.getExifMake()==compareImg.getExifMake() &&
448  srcImg.getSize()==compareImg.getSize())
449  {
450  image=j;
451  break;
452  };
453  };
454  if(image!=-1)
455  {
456  lenses.switchParts(i,lenses.getPartNumber(image));
457  };
458  };
459  std::cout << std::endl << "Assigned " << lenses.getNumberOfParts() << " lenses." << std::endl;
460  if(lenses.getNumberOfParts()>1 && stackLength!=1)
461  {
462  std::cout << "Project contains more than one lens, but you requested to assign" << std::endl
463  << "stacks. This is not supported. Therefore stacks will not be" << std::endl
464  << "assigned." << std::endl << std::endl;
465  stackLength=1;
466  };
467 
468  if (stackLength == 0)
469  {
470  // automatic detection
471  if (pano.hasPossibleStacks())
472  {
473  pano.linkPossibleStacks(linkStacks);
474  };
475  }
476  else
477  {
478  if (stackLength > 1)
479  {
480  stackLength = std::min<int>(stackLength, pano.getNrOfImages());
481  int stackCount = pano.getNrOfImages() / stackLength;
482  if (pano.getNrOfImages() % stackLength > 0)
483  {
484  stackCount++;
485  };
486  if (stackCount < pano.getNrOfImages())
487  {
488  for (size_t stackNr = 0; stackNr < stackCount; stackNr++)
489  {
490  size_t firstImgStack = stackNr*stackLength;
491  for (size_t i = 0; i < stackLength; i++)
492  {
493  if (firstImgStack + i < pano.getNrOfImages())
494  {
495  pano.linkImageVariableStack(firstImgStack, firstImgStack + i);
496  if (linkStacks)
497  {
498  pano.linkImageVariableYaw(firstImgStack, firstImgStack + i);
499  pano.linkImageVariablePitch(firstImgStack, firstImgStack + i);
500  pano.linkImageVariableRoll(firstImgStack, firstImgStack + i);
501  pano.linkImageVariableX(firstImgStack, firstImgStack + i);
502  pano.linkImageVariableY(firstImgStack, firstImgStack + i);
503  pano.linkImageVariableZ(firstImgStack, firstImgStack + i);
504  pano.linkImageVariableTranslationPlaneYaw(firstImgStack, firstImgStack + i);
505  pano.linkImageVariableTranslationPlanePitch(firstImgStack, firstImgStack + i);
506  };
507  };
508  };
509  };
510  };
511  };
512  };
513 
514  variable_groups.update();
515  const size_t stackCount = variable_groups.getStacks().getNumberOfParts();
516  if (stackCount != pano.getNrOfImages())
517  {
518  std::cout << "Assigned " << stackCount << " stacks: " << std::endl
519  << "\t" << (linkStacks ? "Linking position of images in stacks" : "Use individual positions of images in stacks") << std::endl;
520  };
521  };
522 
523  //set output exposure value
526  pano.setOptions(opt);
527  // set optimizer switches
530  pano.updateOptimizeVector();
531 
532  //output
533  if(output=="")
534  {
535  output=hugin_utils::stripExtension(pano.getImage(0).getFilename());
536  if(pano.getNrOfImages()>1)
537  {
538  output.append("-");
539  output.append(hugin_utils::stripExtension(hugin_utils::stripPath(pano.getImage(pano.getNrOfImages()-1).getFilename())));
540  };
541  output=output.append(".pto");
542  };
543  output = hugin_utils::GetAbsoluteFilename(output);
544  //write output
545  if (pano.WritePTOFile(output, hugin_utils::getPathPrefix(output)))
546  {
547  std::cout << std::endl << "Written output to " << output << std::endl;
548  };
549 
551  return 0;
552 }
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 IsLinearICCProfile(const vigra::ImageImportInfo::ICCProfile &iccProfile)
return true if icc profile is linear one, otherwise return false
Definition: utils.cpp:940
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:970
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