Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
tca_correct.cpp
Go to the documentation of this file.
1 // -*- c-basic-offset: 4 -*-
2 
27 #include <hugin_config.h>
28 #include <fstream>
29 #include <sstream>
30 #include <iostream>
31 
32 #include <vigra/error.hxx>
33 #include <vigra_ext/impexalpha.hxx>
34 #include <vigra/cornerdetection.hxx>
35 #include <vigra/localminmax.hxx>
36 #include <hugin_utils/utils.h>
37 #include <hugin_math/hugin_math.h>
38 
39 #include "vigra/stdimage.hxx"
40 #include "vigra/stdimagefunctions.hxx"
41 #include "vigra/functorexpression.hxx"
42 #include "vigra/transformimage.hxx"
43 
44 #include <vigra_ext/Pyramid.h>
45 #include <vigra_ext/Correlation.h>
47 #include <vigra_ext/utils.h>
48 
49 #include <panodata/Panorama.h>
53 #include <nona/Stitcher.h>
54 #include <foreign/levmar/levmar.h>
56 #include <lensdb/LensDB.h>
57 
58 #include <getopt.h>
59 #ifndef _WIN32
60 #include <unistd.h>
61 #endif
62 
63 #include <tiff.h>
64 
65 #define DEFAULT_OPTIMISATION_PARAMETER "abcvde"
66 
67 struct Parameters
68 {
70  {
71  cpErrorThreshold = 1.5;
72  optMethod = 0;
73  load = false;
74  reset = false;
75  saveDB = false;
76  scale=2;
77  nPoints=10;
78  grid = 10;
79  verbose = 0;
80  }
81 
82  double cpErrorThreshold;
83  int optMethod;
84  bool load;
85  bool reset;
86  bool saveDB;
87  std::set<std::string> optvars;
88 
89  std::string alignedPrefix;
90  std::string ptoFile;
91  std::string ptoOutputFile;
92  std::string basename;
93 
94  std::string red_name;
95  std::string green_name;
96  std::string blue_name;
97 
98  double scale;
99  int nPoints;
100  unsigned grid;
101  int verbose;
102 };
103 
105 
106 // Optimiser code
107 struct OptimData
108 {
110  double huberSigma;
112 
113  double m_dist[3][3]; // a,b,c for all imgs
114  double m_shift[2]; // x,y shift
115  double m_hfov[3];
116  double m_center[2]; // center of image (without shift)
117  std::vector<double*> m_mapping;
118 
120 
122  double mEstimatorSigma, int maxIter)
123  : m_pano(pano), huberSigma(mEstimatorSigma), m_optvars(optvars), m_maxIter(maxIter)
124  {
125  assert(m_pano.getNrOfImages() == m_optvars.size());
126  assert(m_pano.getNrOfImages() == 3);
127  LoadFromImgs();
128 
129  for (unsigned int i = 0; i<3; i++)
130  {
131  const std::set<std::string> vars = m_optvars[i];
132  for (std::set<std::string>::const_iterator it = vars.begin(); it != vars.end(); ++it)
133  {
134  const char var = (*it)[0];
135  if ((var >= 'a') && (var <= 'c'))
136  {
137  m_mapping.push_back(&(m_dist[i][var - 'a']));
138  }
139  else if ((var == 'd') || (var == 'e'))
140  {
141  m_mapping.push_back(&(m_shift[var - 'd']));
142  }
143  else if (var == 'v')
144  {
145  m_mapping.push_back(&(m_hfov[i]));
146  }
147  else
148  {
149  std::cerr << "Unknown parameter detected, ignoring!" << std::endl;
150  }
151  }
152  }
153  }
154 
156  void ToX(double* x)
157  {
158  for (size_t i=0; i < m_mapping.size(); i++)
159  {
160  x[i] = *(m_mapping[i]);
161  }
162  }
163 
165  void FromX(double* x)
166  {
167  for (size_t i=0; i < m_mapping.size(); i++)
168  {
169  *(m_mapping[i]) = x[i];
170  }
171  }
172 
174  {
175  for (unsigned int i = 0; i < 3; i++)
176  {
178  m_hfov[i] = img.getHFOV();
179  m_dist[i][0] = img.getRadialDistortion()[0];
180  m_dist[i][1] = img.getRadialDistortion()[1];
181  m_dist[i][2] = img.getRadialDistortion()[2];
182  if (i == 0)
183  {
184  m_shift[0] = img.getRadialDistortionCenterShift().x;
185  m_shift[1] = img.getRadialDistortionCenterShift().y;
186  m_center[0] = img.getSize().width() / 2.0;
187  m_center[1] = img.getSize().height() / 2.0;
188  }
189  }
190  };
191  void SaveToImgs()
192  {
193  for (unsigned int i = 0; i < 3; i++)
194  {
196  img.setHFOV(m_hfov[i]);
197  std::vector<double> radialDist(4);
198  radialDist[0] = m_dist[i][0];
199  radialDist[1] = m_dist[i][1];
200  radialDist[2] = m_dist[i][2];
201  radialDist[3] = 1 - radialDist[0] - radialDist[1] - radialDist[2];
202  img.setRadialDistortion(radialDist);
203  img.setRadialDistortionCenterShift(hugin_utils::FDiff2D(m_shift[0], m_shift[1]));
204  m_pano.setSrcImage(i, img);
205  }
206  };
207 };
208 
210 {
212  std::set<std::string> vars = g_param.optvars;
213  optvars.push_back(vars);
214  optvars.push_back(std::set<std::string>());
215  /* NOTE: delete "d" and "e" if they should be optimized,
216  they are linked and always will be */
217  vars.erase("d");
218  vars.erase("e");
219  optvars.push_back(vars);
220  _retval = optvars;
221 }
222 
223 // dummy panotools progress functions
224 static int ptProgress(int command, char* argument)
225 {
226  return 1;
227 }
228 static int ptinfoDlg(int command, char* argument)
229 {
230  return 1;
231 }
232 
233 // Method 0: using PTOptimizer
234 // PTOptimizer minimizes the tangential and sagittal distance between the points
236 {
237  if (g_param.verbose == 0)
238  {
239  // deactive PTOptimizer status information if -v is not given
240  PT_setProgressFcn(ptProgress);
241  PT_setInfoDlgFcn(ptinfoDlg);
242  };
243 
245  get_optvars(optvars);
246  pano.setOptimizeVector(optvars);
248  return 0;
249 }
250 
251 inline double weightHuber(double x, double sigma)
252 {
253  if (fabs(x) > sigma)
254  {
255  x = sqrt(sigma*(2.0*fabs(x) - sigma));
256  }
257  return x;
258 }
259 
260 void optGetError(double* p, double* x, int m, int n, void* data)
261 {
262  OptimData* dat = static_cast<OptimData*>(data);
263  dat->FromX(p);
264 
265  /* compute new a,b,c,d from a,b,c,v */
266  double dist[3][4];
267  for (unsigned int i = 0; i<3; i++)
268  {
269  double scale = dat->m_hfov[1] / dat->m_hfov[i];
270  for (unsigned int j = 0; j<3; j++)
271  {
272  dist[i][j] = dat->m_dist[i][j] * pow(scale, (int)(4 - j));
273  }
274  dist[i][3] = scale*(1 - dat->m_dist[i][0] - dat->m_dist[i][1] - dat->m_dist[i][2]);
275  }
276 
277  double center[2];
278  center[0] = dat->m_center[0] + dat->m_shift[0];
279  center[1] = dat->m_center[1] + dat->m_shift[1];
280 
281  double base_size = std::min(dat->m_center[0], dat->m_center[1]);
282 
283  HuginBase::CPVector newCPs;
284 
285  unsigned int noPts = dat->m_pano.getNrOfCtrlPoints();
286  // loop over all points to calculate the error
287  for (unsigned int ptIdx = 0; ptIdx < noPts; ptIdx++)
288  {
289  const HuginBase::ControlPoint& cp = dat->m_pano.getCtrlPoint(ptIdx);
290 
291  double dist_p1 = vigra::hypot(cp.x1 - center[0], cp.y1 - center[1]);
292  double dist_p2 = vigra::hypot(cp.x2 - center[0], cp.y2 - center[1]);
293 
294  if (cp.image1Nr == 1)
295  {
296  double base_dist = dist_p1 / base_size;
297  double corr_dist_p1 = dist[cp.image2Nr][0] * pow(base_dist, 4) +
298  dist[cp.image2Nr][1] * pow(base_dist, 3) +
299  dist[cp.image2Nr][2] * pow(base_dist, 2) +
300  dist[cp.image2Nr][3] * base_dist;
301  corr_dist_p1 *= base_size;
302  x[ptIdx] = corr_dist_p1 - dist_p2;
303  }
304  else
305  {
306  double base_dist = dist_p2 / base_size;
307  double corr_dist_p2 = dist[cp.image1Nr][0] * pow(base_dist, 4) +
308  dist[cp.image1Nr][1] * pow(base_dist, 3) +
309  dist[cp.image1Nr][2] * pow(base_dist, 2) +
310  dist[cp.image1Nr][3] * base_dist;
311  corr_dist_p2 *= base_size;
312  x[ptIdx] = corr_dist_p2 - dist_p1;
313  }
314 
315  HuginBase::ControlPoint newcp = cp;
316  newcp.error = fabs(x[ptIdx]);
317  newCPs.push_back(newcp);
318 
319  dat->m_pano.getCtrlPoint(ptIdx);
320  // use huber robust estimator
321  if (dat->huberSigma > 0)
322  {
323  x[ptIdx] = weightHuber(x[ptIdx], dat->huberSigma);
324  }
325  }
326 
327  dat->m_pano.updateCtrlPointErrors(newCPs);
328 }
329 
330 int optVis(double* p, double* x, int m, int n, int iter, double sqerror, void* data)
331 {
332  return 1;
333  /* OptimData * dat = (OptimData *) data;
334  char tmp[200];
335  tmp[199] = 0;
336  double error = sqrt(sqerror/n)*255;
337  snprintf(tmp,199, "Iteration: %d, error: %f", iter, error);
338  return dat->m_progress.increaseProgress(0.0, tmp) ? 1 : 0 ; */
339 }
340 
341 // Method 1: minimize only the center distance difference (sagittal distance) of the points
342 // the tangential distance is not of interest for TCA correction,
343 // and is caused by the limited accuracy of the fine tune function, especially close the the edge of the fisheye image
345 {
347  get_optvars(optvars);
348 
349  int nMaxIter = 1000;
350  OptimData data(pano, optvars, 0.5, nMaxIter);
351 
352  int ret;
353  double info[LM_INFO_SZ];
354 
355  // parameters
356  int m = data.m_mapping.size();
357  vigra::ArrayVector<double> p(m, 0.0);
358 
359  // vector for errors
360  int n = pano.getNrOfCtrlPoints();
361  vigra::ArrayVector<double> x(n, 0.0);
362 
363  data.ToX(p.begin());
364  if (g_param.verbose > 0)
365  {
366  fprintf(stderr, "Parameters before optimization: ");
367  for (int i = 0; i<m; ++i)
368  {
369  fprintf(stderr, "%.7g ", p[i]);
370  }
371  fprintf(stderr, "\n");
372  }
373 
374  ret = dlevmar_dif(&optGetError, &optVis, &(p[0]), &(x[0]), m, n, nMaxIter, NULL, info, NULL, NULL, &data); // no jacobian
375  // copy to source images (data.m_imgs)
376  data.SaveToImgs();
377  // calculate error at solution
378  data.huberSigma = 0;
379  optGetError(&(p[0]), &(x[0]), m, n, &data);
380  double error = 0;
381  for (int i = 0; i<n; i++)
382  {
383  error += x[i] * x[i];
384  }
385  error = sqrt(error / n);
386 
387  if (g_param.verbose > 0)
388  {
389  fprintf(stderr, "Levenberg-Marquardt returned %d in %g iter, reason %g\nSolution: ", ret, info[5], info[6]);
390  for (int i = 0; i<m; ++i)
391  {
392  fprintf(stderr, "%.7g ", p[i]);
393  }
394  fprintf(stderr, "\n\nMinimization info:\n");
395  for (int i = 0; i<LM_INFO_SZ; ++i)
396  {
397  fprintf(stderr, "%g ", info[i]);
398  }
399  fprintf(stderr, "\n");
400  }
401 }
402 
403 static void usage(const char* name)
404 {
405  std::cout << name << ": Parameter estimation of transverse chromatic abberations" << std::endl
406  << name << " version " << hugin_utils::GetHuginVersion() << std::endl
407  << std::endl
408  << "Usage: " << name << " [options] <inputfile>" << std::endl
409  << " option are: " << std::endl
410  << " -h Display help (this text)" << std::endl
411  << " -l input file is PTO file instead of image" << std::endl
412  << " -m method optimization method (0 normal, 1 newfit)" << std::endl
413  << " -o optvars string of variables to optimize (\"abcvde\")" << std::endl
414  << " -r reset values (this will zero a,b,c,d,e params and set v to 10)" << std::endl
415  << " makes sense only with -l option" << std::endl
416  << " -s <scale> Scale for corner detection" << std::endl
417  << " -n <number> number of points per grid cell (default: 10)" << std::endl
418  << " -g <number> divide image in <number>x<number> grid cells (default: 10)" << std::endl
419  << " -t num Remove all control points with an error higher than num pixels (default: 1.5)" << std::endl
420  << " -v Verbose" << std::endl
421  << " --save-into-database Saves the tca data into Hugin lens database" << std::endl
422  << " -w filename write PTO file" << std::endl
423  << " -R <r> Use this file as red channel" << std::endl
424  << " -G <g> Use this file as green channel" << std::endl
425  << " -B <b> Use this file as blue channel" << std::endl
426  << std::endl
427  << " <inputfile> is the base name of 4 image files:" << std::endl
428  << " <inputfile> Colour file to compute TCA parameters" << std::endl
429  << " red_<inputfile> Red channel of <inputfile>" << std::endl
430  << " green_<inputfile> Green channel of <inputfile>" << std::endl
431  << " blue_<inputfile> Blue channel of <inputfile>" << std::endl
432  << " The channel images must be colour images with 3 identical channels." << std::endl
433  << " If any of -R, -G, or -B is given, this file name is used instead of the derived name." << std::endl
434  << std::endl
435  << " Output:" << std::endl
436  << " commandline arguments for fulla" << std::endl;
437 }
438 
440 typedef std::multimap<double, vigra::Diff2D> MapPoints;
441 
442 template <class ImageType>
443 void createCtrlPoints(HuginBase::Panorama& pano, const ImageType& img, int imgRedNr, int imgGreenNr, int imgBlueNr, double scale, int nPoints, unsigned grid)
444 {
445  vigra::BasicImage<vigra::RGBValue<vigra::UInt8> > img8(img.size());
446 
449  vigra::functor::Arg1()*vigra::functor::Param(ratio));
450  if (g_param.verbose > 0)
451  {
452  std::cout << "image8 size:" << img8.size() << std::endl;
453  };
455  // find interesting corners using harris corner detector
456 
457  if (g_param.verbose > 0)
458  {
459  std::cout << "Finding control points... " << std::endl;
460  }
461  const long templWidth = 29;
462  const long sWidth = 29 + 11;
463 
464 
465  vigra::Size2D size(img8.width(), img8.height());
466  std::vector<vigra::Rect2D> rects;
467  for (unsigned party = 0; party < grid; party++)
468  {
469  for (unsigned partx = 0; partx < grid; partx++)
470  {
471  // run corner detector only in current sub-region (saves a lot of memory for big images)
472  vigra::Rect2D rect(partx*size.x / grid, party*size.y / grid,
473  (partx + 1)*size.x / grid, (party + 1)*size.y / grid);
474  rect &= vigra::Rect2D(size);
475  if (rect.width()>0 && rect.height()>0)
476  {
477  rects.push_back(rect);
478  };
479  };
480  };
481 
482  #pragma omp parallel for schedule(dynamic)
483  for (int i = 0; i < rects.size(); ++i)
484  {
485  MapPoints points;
486  vigra::Rect2D rect(rects[i]);
487  vigra_ext::findInterestPointsPartial(vigra::srcImageRange(img8, vigra::GreenAccessor<vigra::RGBValue<vigra::UInt8> >()), rect, scale, 5 * nPoints, points);
488 
489  // loop over all points, starting with the highest corner score
491  size_t nBad = 0;
492  for (MapPoints::const_reverse_iterator it = points.rbegin(); it != points.rend(); ++it)
493  {
494  if (cps.size() >= nPoints)
495  {
496  // we have enough points, stop
497  break;
498  };
499 
500  // Green <-> Red
501  HuginBase::ControlPoint p1(imgGreenNr, it->second.x, it->second.y, imgRedNr, it->second.x, it->second.y);
502  vigra::Diff2D roundP1(hugin_utils::roundi(p1.x1), hugin_utils::roundi(p1.y1));
503  vigra::Diff2D roundP2(hugin_utils::roundi(p1.x2), hugin_utils::roundi(p1.y2));
504 
506  img8, vigra::GreenAccessor<vigra::RGBValue<vigra::UInt8> >(),
507  roundP1, templWidth,
508  img8, vigra::RedAccessor<vigra::RGBValue<vigra::UInt8> >(),
509  roundP2, sWidth);
510 
511  if (res.maxi > 0.98)
512  {
513  p1.x1 = roundP1.x;
514  p1.y1 = roundP1.y;
515  p1.x2 = res.maxpos.x;
516  p1.y2 = res.maxpos.y;
517  p1.error = res.maxi;
518  cps.push_back(p1);
519  }
520  else
521  {
522  ++nBad;
523  };
524 
525  // Green <-> Blue
526  HuginBase::ControlPoint p2(imgGreenNr, it->second.x, it->second.y, imgBlueNr, it->second.x, it->second.y);
527  roundP1 = vigra::Diff2D(hugin_utils::roundi(p2.x1), hugin_utils::roundi(p2.y1));
528  roundP2 = vigra::Diff2D(hugin_utils::roundi(p2.x2), hugin_utils::roundi(p2.y2));
529 
531  img8, vigra::GreenAccessor<vigra::RGBValue<vigra::UInt8> >(), roundP1, templWidth,
532  img8, vigra::BlueAccessor<vigra::RGBValue<vigra::UInt8> >(), roundP2, sWidth);
533 
534  if (res.maxi > 0.98)
535  {
536  p2.x1 = roundP1.x;
537  p2.y1 = roundP1.y;
538  p2.x2 = res.maxpos.x;
539  p2.y2 = res.maxpos.y;
540  p2.error = res.maxi;
541  cps.push_back(p2);
542  }
543  else
544  {
545  ++nBad;
546  };
547  }
548  if (g_param.verbose > 0)
549  {
550  std::ostringstream buf;
551  buf << "Number of good matches: " << cps.size() << ", bad matches: " << nBad << std::endl;
552  std::cout << buf.str();
553  }
554  if (!cps.empty())
555  {
556  hugin_omp::ScopedLock sl(lock);
557  for (HuginBase::CPVector::const_iterator it = cps.begin(); it != cps.end(); ++it)
558  {
559  pano.addCtrlPoint(*it);
560  };
561  };
562  };
563 };
564 
565 int main2(HuginBase::Panorama& pano);
566 
567 template <class PixelType>
568 int processImg(const char* filename)
569 {
570  typedef vigra::BasicImage<PixelType> ImageType;
571  try
572  {
573  // load first image
574  vigra::ImageImportInfo imgInfo(filename);
575  ImageType imgOrig(imgInfo.size());
576 
577  const int bands = imgInfo.numBands();
578  const int extraBands = imgInfo.numExtraBands();
579 
580  if (!(bands == 3 || (bands == 4 && extraBands == 1)))
581  {
582  std::cerr << "Unsupported number of bands!";
583  exit(-1);
584  }
585 
586  if (bands == 3)
587  {
588  vigra::importImage(imgInfo, vigra::destImage(imgOrig));
589  }
590  else
591  {
592  vigra::BImage alpha(imgInfo.size());
593  vigra::importImageAlpha(imgInfo, destImage(imgOrig), destImage(alpha));
594  };
595  HuginBase::Panorama pano;
596  // add the first image to the panorama object
597  HuginBase::StandardImageVariableGroups variable_groups(pano);
598  HuginBase::ImageVariableGroup& lenses = variable_groups.getLenses();
599 
600  std::string red_name;
601  if( g_param.red_name.size())
602  {
603  red_name=g_param.red_name;
604  }
605  else
606  {
607  red_name=std::string("red_")+filename;
608  }
609 
610  HuginBase::SrcPanoImage srcRedImg;
611  srcRedImg.setSize(imgInfo.size());
612  srcRedImg.setProjection(HuginBase::SrcPanoImage::RECTILINEAR);
613  srcRedImg.setHFOV(10);
614  srcRedImg.setCropFactor(1);
615  srcRedImg.setFilename(red_name);
616  int imgRedNr = pano.addImage(srcRedImg);
617  lenses.updatePartNumbers();
618  lenses.switchParts(imgRedNr, 0);
619 
620  std::string green_name;
621  if( g_param.green_name.size())
622  {
623  green_name=g_param.green_name;
624  }
625  else
626  {
627  green_name=std::string("green_")+filename;
628  }
629 
630  HuginBase::SrcPanoImage srcGreenImg;
631  srcGreenImg.setSize(imgInfo.size());
632  srcGreenImg.setProjection(HuginBase::SrcPanoImage::RECTILINEAR);
633  srcGreenImg.setHFOV(10);
634  srcGreenImg.setCropFactor(1);
635  srcGreenImg.setFilename(green_name);
636  int imgGreenNr = pano.addImage(srcGreenImg);
637  lenses.updatePartNumbers();
638  lenses.switchParts(imgGreenNr, 0);
639 
640  std::string blue_name;
641  if( g_param.blue_name.size())
642  {
643  blue_name=g_param.blue_name;
644  }
645  else
646  {
647  blue_name=std::string("blue_")+filename;
648  }
649 
650  HuginBase::SrcPanoImage srcBlueImg;
651  srcBlueImg.setSize(imgInfo.size());
652  srcBlueImg.setProjection(HuginBase::SrcPanoImage::RECTILINEAR);
653  srcBlueImg.setHFOV(10);
654  srcBlueImg.setCropFactor(1);
655  srcBlueImg.setFilename(blue_name);
656  int imgBlueNr = pano.addImage(srcBlueImg);
657  lenses.updatePartNumbers();
658  lenses.switchParts(imgBlueNr, 0);
659 
660  // lens variables are linked by default. Unlink the field of view and
661  // the radial distortion.
662  lenses.unlinkVariablePart(HuginBase::ImageVariableGroup::IVE_HFOV, 0);
663  lenses.unlinkVariablePart(HuginBase::ImageVariableGroup::IVE_RadialDistortion, 0);
664 
665  // setup output to be exactly similar to input image
668  opts.setHFOV(srcGreenImg.getHFOV(), false);
669  opts.setWidth(srcGreenImg.getSize().x, false);
670  opts.setHeight(srcGreenImg.getSize().y);
671 
672  // output to tiff format
674  opts.tiff_saveROI = false;
675  // m estimator, to be more robust against points on moving objects
676  opts.huberSigma = 0.5;
677  pano.setOptions(opts);
678 
679  createCtrlPoints(pano, imgOrig, imgRedNr, imgGreenNr, imgBlueNr, g_param.scale, g_param.nPoints, g_param.grid);
680 
681  main2(pano);
682  }
683  catch (std::exception& e)
684  {
685  std::cerr << "ERROR: caught exception: " << e.what() << std::endl;
686  return 1;
687  }
688  return 0;
689 }
690 
691 int processPTO(const char* filename)
692 {
693  HuginBase::Panorama pano;
694 
695  const std::string filenameString(filename);
696  if (!pano.ReadPTOFile(filenameString, hugin_utils::getPathPrefix(filenameString)))
697  {
698  return 1;
699  };
700 
701  return main2(pano);
702 }
703 
705 {
706  for (unsigned int i=0; i < 3; i++)
707  {
708  HuginBase::SrcPanoImage img = pano.getSrcImage(i);
709 
710  img.setHFOV(10);
711 
712  std::vector<double> radialDist(4);
713  radialDist[0] = 0;
714  radialDist[1] = 0;
715  radialDist[2] = 0;
716  radialDist[3] = 1;
717  img.setRadialDistortion(radialDist);
718 
719  img.setRadialDistortionCenterShift(hugin_utils::FDiff2D(0, 0));
720 
721  pano.setSrcImage(i, img);
722  }
723 }
724 
726 {
727  double dist[3][3]; // a,b,c for all imgs
728  double shift[2]; // x,y shift
729  double hfov[3];
730 
731  for (unsigned int i=0; i < 3; i++)
732  {
733  HuginBase::SrcPanoImage img = pano.getSrcImage(i);
734  hfov[i] = img.getHFOV();
735  dist[i][0] = img.getRadialDistortion()[0];
736  dist[i][1] = img.getRadialDistortion()[1];
737  dist[i][2] = img.getRadialDistortion()[2];
738  if (i == 0)
739  {
740  shift[0] = img.getRadialDistortionCenterShift().x;
741  shift[1] = img.getRadialDistortionCenterShift().y;
742  }
743  }
744 
745  /* compute new a,b,c,d from a,b,c,v */
746  double distnew[3][4];
747  for (unsigned int i=0 ; i<3 ; i++)
748  {
749  double scale = hfov[1] / hfov[i];
750  for (unsigned int j=0 ; j<3 ; j++)
751  {
752  distnew[i][j] = dist[i][j]*pow(scale, (int)(4-j));
753  }
754  distnew[i][3] = scale*(1 - dist[i][0] - dist[i][1] - dist[i][2]);
755  }
756 
757  if (hugin_utils::roundi(shift[0]) == 0 && hugin_utils::roundi(shift[1]) == 0)
758  {
759  fprintf(stdout, "-r %.7f:%.7f:%.7f:%.7f "
760  "-b %.7f:%.7f:%.7f:%.7f ",
761  distnew[0][0], distnew[0][1], distnew[0][2], distnew[0][3],
762  distnew[2][0], distnew[2][1], distnew[2][2], distnew[2][3]);
763  }
764  else
765  {
766  fprintf(stdout, "-r %.7f:%.7f:%.7f:%.7f "
767  "-b %.7f:%.7f:%.7f:%.7f "
768  "-x %d:%d\n",
769  distnew[0][0], distnew[0][1], distnew[0][2], distnew[0][3],
770  distnew[2][0], distnew[2][1], distnew[2][2], distnew[2][3],
771  hugin_utils::roundi(shift[0]), hugin_utils::roundi(shift[1]));
772  };
773  if (g_param.saveDB)
774  {
775  // save information into database
776  // read EXIF data, using routines of HuginBase::SrcPanoImage
778  img.setFilename(g_param.basename);
779  img.readEXIF();
780  std::string lensname = img.getDBLensName();
781  if (lensname.empty())
782  {
783  // no suitable lensname found in exif data, ask user
784  std::cout << std::endl << "For saving information tca data into database no suitable information" << std::endl
785  << "found in EXIF data." << std::endl
786  << "For fixed lens cameras leave lensname empty." << std::endl << std::endl << "Lensname: ";
787  char input[256];
788  std::cin.getline(input, 255);
789  lensname = hugin_utils::StrTrim(std::string(input));
790  if (lensname.empty())
791  {
792  std::string camMaker;
793  std::string camModel;
794  std::cout << "Camera maker: ";
795  while (camMaker.empty())
796  {
797  std::cin.getline(input, 255);
798  camMaker = hugin_utils::StrTrim(std::string(input));
799  };
800  std::cout << "Camera model: ";
801  while (camModel.empty())
802  {
803  std::cin.getline(input, 255);
804  camModel = hugin_utils::StrTrim(std::string(input));
805  };
806  lensname = camMaker.append("|").append(camModel);
807  };
808  };
809  double focal = img.getExifFocalLength();
810  if (fabs(focal) < 0.1f)
811  {
812  std::cout << "Real focal length (in mm): ";
813  while (fabs(focal) < 0.1f)
814  {
815  std::cin >> focal;
816  focal = fabs(focal);
817  };
818  };
820  std::vector<double> redDistData(4, 0.0f);
821  std::vector<double> blueDistData(4, 0.0f);
822  redDistData[0] = distnew[0][0];
823  redDistData[1] = distnew[0][1];
824  redDistData[2] = distnew[0][2];
825  redDistData[3] = distnew[0][3];
826  blueDistData[0] = distnew[2][0];
827  blueDistData[1] = distnew[2][1];
828  blueDistData[2] = distnew[2][2];
829  blueDistData[3] = distnew[2][3];
830  if (lensDB.SaveTCA(lensname, focal, redDistData, blueDistData))
831  {
832  std::cout << std::endl << std::endl << "TCA data for " << lensname << " @ " << focal << " mm successful saved into database." << std::endl;
833  }
834  else
835  {
836  std::cout << std::endl << std::endl << "Could not save data into database." << std::endl;
837  };
839  }
840 }
841 
843 {
844  if (g_param.reset)
845  {
846  resetValues(pano);
847  }
848 
849  for (int i = 0; i < 10; i++)
850  {
851  if (g_param.optMethod == 0)
852  {
853  optimize_old(pano);
854  }
855  else if (g_param.optMethod == 1)
856  {
857  optimize_new(pano);
858  }
859  else
860  {
861  std::cerr << "Unknown optimizer strategy." << std::endl
862  << "Using newfit method." << std::endl;
863  g_param.optMethod = 1;
864  optimize_new(pano);
865  };
866 
867  HuginBase::CPVector cps = pano.getCtrlPoints();
868  HuginBase::CPVector newCPs;
869  for (HuginBase::CPVector::const_iterator it = cps.begin(); it != cps.end(); ++it)
870  {
871  if (it->error < g_param.cpErrorThreshold)
872  {
873  newCPs.push_back(*it);
874  }
875  }
876  if (g_param.verbose > 0)
877  {
878  std::cerr << "Ctrl points before pruning: " << cps.size() << ", after: " << newCPs.size() << std::endl;
879  }
880  pano.setCtrlPoints(newCPs);
881 
882  if (cps.size() == newCPs.size())
883  {
884  // no points were removed, do not re-optimize
885  break;
886  }
887  }
888 
889  if (!g_param.ptoOutputFile.empty())
890  {
892  get_optvars(optvars);
893  HuginBase::UIntSet allImgs;
894  fill_set(allImgs, 0, pano.getNrOfImages() - 1);
895  std::ofstream script(g_param.ptoOutputFile.c_str());
896  pano.printPanoramaScript(script, optvars, pano.getOptions(), allImgs, true, "");
897  }
898 
899  print_result(pano);
900  return 0;
901 }
902 
903 int main(int argc, char* argv[])
904 {
905  // parse arguments
906  const char* optstring = "hlm:o:rt:vw:R:G:B:s:g:n:";
907  enum
908  {
909  SAVEDATABASE=1000
910  };
911  static struct option longOptions[] =
912  {
913  { "save-into-database", no_argument, NULL, SAVEDATABASE },
914  { "help", no_argument, NULL, 'h' },
915  0
916  };
917  int c;
918  bool parameter_request_seen=false;
919 
920  while ((c = getopt_long(argc, argv, optstring, longOptions, nullptr)) != -1)
921  {
922  switch (c)
923  {
924  case 'h':
925  usage(hugin_utils::stripPath(argv[0]).c_str());
926  return 0;
927  case 'l':
928  g_param.load = true;
929  break;
930  case 'm':
931  g_param.optMethod = atoi(optarg);
932  break;
933  case 'o':
934  {
935  char* optptr = optarg;
936  while (*optptr != 0)
937  {
938  if ((*optptr == 'a') || (*optptr == 'b') ||
939  (*optptr == 'c') || (*optptr == 'v') ||
940  (*optptr == 'd') || (*optptr == 'e'))
941  {
942  g_param.optvars.insert(std::string(optptr, 1));
943  }
944  optptr++;
945  }
946  parameter_request_seen=true;
947  }
948  break;
949  case 'r':
950  g_param.reset = true;
951  break;
952  case 't':
953  g_param.cpErrorThreshold = atof(optarg);
954  if (g_param.cpErrorThreshold <= 0)
955  {
956  std::cerr << hugin_utils::stripPath(argv[0]) << ": Invalid parameter: control point error threshold (-t) must be greater than 0" << std::endl;
957  return 1;
958  }
959  break;
960  case 'v':
961  g_param.verbose++;
962  break;
963  case 'w':
964  g_param.ptoOutputFile = optarg;
965  break;
966  case 'R':
967  g_param.red_name = optarg;
968  break;
969  case 'G':
970  g_param.green_name = optarg;
971  break;
972  case 'B':
973  g_param.blue_name = optarg;
974  break;
975  case 's':
976  g_param.scale=atof( optarg);
977  break;
978  case 'n':
979  g_param.nPoints=atoi( optarg);
980  break;
981  case 'g':
982  g_param.grid=atoi(optarg);
983  break;
984  case SAVEDATABASE:
985  g_param.saveDB = true;
986  break;
987  case ':':
988  case '?':
989  // missing argument or invalid switch
990  return 1;
991  break;
992  default:
993  // this should not happen
994  abort();
995  }
996  };
997 
998  if (argc - optind != 1)
999  {
1000  if (argc - optind < 1)
1001  {
1002  std::cerr << hugin_utils::stripPath(argv[0]) << ": No input file given." << std::endl;
1003  }
1004  else
1005  {
1006  std::cerr << hugin_utils::stripPath(argv[0]) << ": Only one input file expected." << std::endl;
1007  };
1008  return 1;
1009  };
1010 
1011  // If no parameters were requested to be optimised, we optimize the
1012  // default parameters.
1013  if ( !parameter_request_seen)
1014  {
1015  for ( const char* dop=DEFAULT_OPTIMISATION_PARAMETER;
1016  *dop != 0; ++dop)
1017  {
1018  g_param.optvars.insert( std::string( dop, 1));
1019  }
1020  }
1021 
1022  // Program will crash if nothing is to be optimised.
1023  if ( g_param.optvars.empty())
1024  {
1025  std::cerr << hugin_utils::stripPath(argv[0]) << ": No parameters to optimize." << std::endl;
1026  return 1;
1027  }
1028 
1029  if (!g_param.load)
1030  {
1031  g_param.basename = argv[optind];
1032  vigra::ImageImportInfo firstImgInfo(argv[optind]);
1033  std::string pixelType = firstImgInfo.getPixelType();
1034  if (pixelType == "UINT8")
1035  {
1036  return processImg<vigra::RGBValue<vigra::UInt8> >(argv[optind]);
1037  }
1038  else if (pixelType == "INT16")
1039  {
1040  return processImg<vigra::RGBValue<vigra::Int16> >(argv[optind]);
1041  }
1042  else if (pixelType == "UINT16")
1043  {
1044  return processImg<vigra::RGBValue<vigra::UInt16> >(argv[optind]);
1045  }
1046  else
1047  {
1048  std::cerr << " ERROR: unsupported pixel type: " << pixelType << std::endl;
1049  return 1;
1050  }
1051  }
1052  else
1053  {
1054  return processPTO(argv[optind]);
1055  }
1056 
1057  return 0;
1058 }
1059 
std::set< std::string > optvars
Definition: tca_correct.cpp:87
int optimize_old(HuginBase::Panorama &pano)
void setHeight(unsigned int h)
set panorama height
int roundi(T x)
Definition: hugin_math.h:73
std::string StrTrim(const std::string &str)
remove trailing and leading white spaces and tabs
Definition: utils.cpp:208
void transformImage(vigra::triple< SrcImageIterator, SrcImageIterator, SrcAccessor > src, vigra::triple< DestImageIterator, DestImageIterator, DestAccessor > dest, std::pair< AlphaImageIterator, AlphaAccessor > alpha, vigra::Diff2D destUL, TRANSFORM &transform, PixelTransform &pixelTransform, bool warparound, Interpolator interpol, AppBase::ProgressDisplay *progress, bool singleThreaded=false)
Transform an image into the panorama.
std::string blue_name
Definition: tca_correct.cpp:96
static LensDB & GetSingleton()
returns the static LensDB instance
Definition: LensDB.cpp:2001
SrcPanoImage getSrcImage(unsigned imgNr) const
get a description of a source image
Definition: Panorama.cpp:1620
virtual void setSrcImage(unsigned int nr, const SrcPanoImage &img)=0
set input image parameters TODO: Propagate changes to linked images.
void FromX(double *x)
copy new values from x to internal optimization variables
void optGetError(double *p, double *x, int m, int n, void *data)
double scale
Definition: tca_correct.cpp:98
void resetValues(HuginBase::Panorama &pano)
misc math function &amp; classes used by other parts of the program
CorrelationResult PointFineTune(const IMAGET &templImg, ACCESSORT access_t, vigra::Diff2D templPos, int templSize, const IMAGES &searchImg, ACCESSORS access_s, vigra::Diff2D searchPos, int sWidth)
fine tune a point with normalized cross correlation
Definition: Correlation.h:504
std::string getDBLensName() const
constructs the lens name for the database it is the lensname if known, for compact cameras it is cons...
Somewhere to specify what variables belong to what.
Parameters g_param
double m_hfov[3]
wraps around PTOptimizer
const CPVector & getCtrlPoints() const
get all control point of this Panorama
Definition: Panorama.h:319
represents a control point
Definition: ControlPoint.h:38
functions to manage ROI&#39;s
void setOptimizeVector(const OptimizeVector &optvec)
set optimize setting
Definition: Panorama.cpp:297
void findInterestPointsPartial(vigra::triple< ImageIter, ImageIter, ImageAcc > img, const vigra::Rect2D &rect, double scale, unsigned nPoints, std::multimap< double, vigra::Diff2D > &points)
static void Clean()
cleanup the static LensDB instance, must be called at the end of the program
Definition: LensDB.cpp:2010
#define DEFAULT_OPTIMISATION_PARAMETER
Definition: tca_correct.cpp:65
double m_dist[3][3]
main database class
Definition: LensDB.h:44
std::set< unsigned int > UIntSet
Definition: PanoramaData.h:51
helper for OpenMP
class to access Hugins camera and lens database
static double sigma
void LoadFromImgs()
std::string green_name
Definition: tca_correct.cpp:95
Model for a panorama.
Definition: Panorama.h:152
std::string red_name
Definition: tca_correct.cpp:94
unsigned int addCtrlPoint(const ControlPoint &point)
add a new control point.
Definition: Panorama.cpp:381
std::string getPathPrefix(const std::string &filename)
Get the path to a filename.
Definition: utils.cpp:184
void print_result(HuginBase::Panorama &pano)
virtual void updateCtrlPointErrors(const CPVector &controlPoints)=0
update control points distances.
bool SaveTCA(const std::string &lens, const double focal, const std::vector< double > &tca_red, const std::vector< double > &tca_blue, const int weight=10)
saves the tca distortion parameters of the lens
Definition: LensDB.cpp:2499
std::size_t getNrOfImages() const
number of images.
Definition: Panorama.h:205
void setCtrlPoints(const CPVector &points)
set all control points (Ippei: Is this supposed to be &#39;add&#39; method?)
Definition: Panorama.cpp:449
vigra::FRGBImage ImageType
float pow(float a, double b)
Definition: utils.h:181
OptimData(HuginBase::PanoramaData &pano, const HuginBase::OptimizeVector &optvars, double mEstimatorSigma, int maxIter)
static int ptinfoDlg(int command, char *argument)
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
Maximum of correlation, position and value.
Definition: Correlation.h:56
void ToX(double *x)
copy internal optimization variables into x
ImageVariableGroup & getLenses()
Get the ImageVariableGroup representing the group of lens variables.
std::vector< double * > m_mapping
Model for a panorama.
Definition: PanoramaData.h:81
std::multimap< double, vigra::Diff2D > MapPoints
int processPTO(const char *filename)
void SaveToImgs()
std::string ptoOutputFile
Definition: tca_correct.cpp:91
virtual const ControlPoint & getCtrlPoint(std::size_t nr) const =0
get a control point, counting starts with 0
double m_center[2]
vigra::pair< typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::ImageAccessor > destImage(ROIImage< Image, Alpha > &img)
Definition: ROIImage.h:324
#define shift
static int ptProgress(int command, char *argument)
void unlinkVariablePart(ImageVariableEnum variable, unsigned int partNr)
unlink one of the variables across a given part.
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
!! from PTOptimise.h 1951
unsigned int addImage(const SrcPanoImage &img)
the the number for a specific image
Definition: Panorama.cpp:319
compute interest points
int processImg(const char *filename)
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
Same as above, but use a non const panorama.
void createCtrlPoints(HuginBase::Panorama &pano, int img1, const ImageType &leftImg, const ImageType &leftImgOrig, int img2, const ImageType &rightImg, const ImageType &rightImgOrig, int pyrLevel, double scale, unsigned nPoints, unsigned grid, double corrThresh=0.9, bool stereo=false)
void setHFOV(double h, bool keepView=true)
set the horizontal field of view.
Contains various routines used for stitching panoramas.
std::string alignedPrefix
const PanoramaOptions & getOptions() const
returns the options for this panorama
Definition: Panorama.h:481
options wxIntPtr wxIntPtr sortData std::vector< PanoInfo > * data
static hugin_omp::Lock lock
void updatePartNumbers()
Update the part numbers, call this when the panorama changes.
static T max(T x, T y)
Definition: svm.cpp:65
static void usage()
Definition: Main.cpp:32
const HuginBase::OptimizeVector & m_optvars
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.
void setSize(vigra::Size2D val)
Set the image size in pixels.
std::string GetHuginVersion()
return a string with version numbers
Definition: utils.cpp:920
int main2(std::vector< std::string > files, Parameters param)
int optVis(double *p, double *x, int m, int n, int iter, double sqerror, void *data)
hugin_utils::FDiff2D maxpos
Definition: Correlation.h:64
std::vector< ControlPoint > CPVector
Definition: ControlPoint.h:99
virtual std::size_t getNrOfCtrlPoints() const =0
number of control points
std::string basename
static void info(const char *fmt,...)
Definition: svm.cpp:95
std::vector< std::set< std::string > > OptimizeVector
unsigned grid
void fill_set(_Container &c, typename _Container::key_type begin, typename _Container::key_type end)
Definition: stl_utils.h:81
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
HuginBase::PanoramaData & m_pano
void printPanoramaScript(std::ostream &o, const OptimizeVector &optvars, const PanoramaOptions &options, const UIntSet &imgs, bool forPTOptimizer, const std::string &stripPrefix="") const
create an optimizer script
virtual SrcPanoImage getSrcImage(unsigned imgNr) const =0
get a complete description of a source image
void setSrcImage(unsigned int nr, const SrcPanoImage &img)
set input image parameters
double m_shift[2]
virtual std::size_t getNrOfImages() const =0
number of images.
void optimize_new(HuginBase::PanoramaData &pano)
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.
void switchParts(unsigned int ImageNr, unsigned int partNr)
switch a given image to a different part number.
static T min(T x, T y)
Definition: svm.cpp:62
double huberSigma
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 get_optvars(HuginBase::OptimizeVector &_retval)
void setWidth(unsigned int w, bool keepView=true)
set panorama width keep the HFOV, if keepView=true
std::string ptoFile
double weightHuber(double x, double sigma)
int main(int argc, char *argv[])
Definition: Main.cpp:167