Hugin trunk 0.1
Loading...
Searching...
No Matches
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>
34#include <vigra/cornerdetection.hxx>
35#include <vigra/localminmax.hxx>
36#include <hugin_utils/utils.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>
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
67struct 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;
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;
102};
103
105
106// Optimiser code
108{
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)
124 {
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 };
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
224static int ptProgress(int command, char* argument)
225{
226 return 1;
227}
228static 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
242 };
243
245 get_optvars(optvars);
246 pano.setOptimizeVector(optvars);
248 return 0;
249}
250
251inline 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
260void 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
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;
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;
313 }
314
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
330int 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
403static 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
440typedef std::multimap<double, vigra::Diff2D> MapPoints;
441
442template <class ImageType>
443void 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
448 transformImage(srcImageRange(img), destImage(img8),
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 {
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> >(),
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 {
557 for (HuginBase::CPVector::const_iterator it = cps.begin(); it != cps.end(); ++it)
558 {
559 pano.addCtrlPoint(*it);
560 };
561 };
562 };
563};
564
565int main2(HuginBase::Panorama& pano);
566
567template <class PixelType>
568int 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 };
596 // add the first image to the panorama object
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
611 srcRedImg.setSize(imgInfo.size());
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
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
651 srcBlueImg.setSize(imgInfo.size());
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
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
691int processPTO(const char* filename)
692{
694
695 const std::string filenameString(filename);
697 {
698 return 1;
699 };
700
701 return main2(pano);
702}
703
705{
706 for (unsigned int i=0; i < 3; i++)
707 {
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 {
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],
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];
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
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);
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
903int 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':
926 return 0;
927 case 'l':
928 g_param.load = true;
929 break;
930 case 'm':
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 }
947 }
948 break;
949 case 'r':
950 g_param.reset = true;
951 break;
952 case 't':
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':
962 break;
963 case 'w':
965 break;
966 case 'R':
968 break;
969 case 'G':
971 break;
972 case 'B':
974 break;
975 case 's':
977 break;
978 case 'n':
980 break;
981 case 'g':
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.
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 {
1032 vigra::ImageImportInfo firstImgInfo(argv[optind]);
1033 std::string pixelType = firstImgInfo.getPixelType();
1034 if (pixelType == "UINT8")
1035 {
1037 }
1038 else if (pixelType == "INT16")
1039 {
1041 }
1042 else if (pixelType == "UINT16")
1043 {
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::multimap< double, vigra::Diff2D > MapPoints
compute interest points
class to access Hugins camera and lens database
static void usage()
Definition Main.cpp:32
!! from PTOptimise.h 1951
wraps around PTOptimizer
#define shift
Somewhere to specify what variables belong to what.
Contains various routines used for stitching panoramas.
represents a control point
Same as above, but use a non const panorama.
main database class
Definition LensDB.h:45
static void Clean()
cleanup the static LensDB instance, must be called at the end of the program
Definition LensDB.cpp:2010
static LensDB & GetSingleton()
returns the static LensDB instance
Definition LensDB.cpp:2001
Model for a panorama.
virtual std::size_t getNrOfCtrlPoints() const =0
number of control points
virtual void setSrcImage(unsigned int nr, const SrcPanoImage &img)=0
set input image parameters TODO: Propagate changes to linked images.
virtual SrcPanoImage getSrcImage(unsigned imgNr) const =0
get a complete description of a source image
virtual std::size_t getNrOfImages() const =0
number of images.
Panorama image options.
void setHFOV(double h, bool keepView=true)
set the horizontal field of view.
void setProjection(ProjectionFormat f)
set the Projection format and adjust the hfov/vfov if nessecary
void setHeight(unsigned int h)
set panorama height
void setWidth(unsigned int w, bool keepView=true)
set panorama width keep the HFOV, if keepView=true
Model for a panorama.
Definition Panorama.h:153
const PanoramaOptions & getOptions() const
returns the options for this panorama
Definition Panorama.h:481
void setOptimizeVector(const OptimizeVector &optvec)
set optimize setting
Definition Panorama.cpp:297
void setCtrlPoints(const CPVector &points)
set all control points (Ippei: Is this supposed to be 'add' method?)
Definition Panorama.cpp:449
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...
SrcPanoImage getSrcImage(unsigned imgNr) const
get a description of a source image
unsigned int addCtrlPoint(const ControlPoint &point)
add a new control point.
Definition Panorama.cpp:381
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
Definition Panorama.cpp:503
unsigned int addImage(const SrcPanoImage &img)
the the number for a specific image
Definition Panorama.cpp:319
void setOptions(const PanoramaOptions &opt)
set new output settings This is not used directly for optimizing/stiching, but it can be feed into ru...
void setSrcImage(unsigned int nr, const SrcPanoImage &img)
set input image parameters
const CPVector & getCtrlPoints() const
get all control point of this Panorama
Definition Panorama.h:319
std::size_t getNrOfImages() const
number of images.
Definition Panorama.h:205
All variables of a source image.
std::string getDBLensName() const
constructs the lens name for the database it is the lensname if known, for compact cameras it is cons...
void setSize(vigra::Size2D val)
Set the image size in pixels.
bool readEXIF()
try to fill out information about the image, by examining the exif data
static double sigma
class AlphaIterator class AlphaAccessor inline void importImageAlpha(const ImageImportInfo &import_info, ImageIterator image_iterator, ImageAccessor image_accessor, AlphaIterator alpha_iterator, AlphaAccessor alpha_accessor)
vigra::FRGBImage ImageType
misc math function & classes used by other parts of the program
unsigned int optimize(PanoramaData &pano, const char *userScript)
optimize the images imgs, for variables optvec, using vars as start.
std::vector< ControlPoint > CPVector
std::vector< std::set< std::string > > OptimizeVector
std::set< unsigned int > UIntSet
std::string StrTrim(const std::string &str)
remove trailing and leading white spaces and tabs
Definition utils.cpp:208
int roundi(T x)
Definition hugin_math.h:73
std::string GetHuginVersion()
return a string with version numbers
Definition utils.cpp:907
std::string stripPath(const std::string &filename)
remove the path of a filename (mainly useful for gui display of filenames)
Definition utils.cpp:160
std::string getPathPrefix(const std::string &filename)
Get the path to a filename.
Definition utils.cpp:184
void findInterestPointsPartial(vigra::triple< ImageIter, ImageIter, ImageAcc > img, const vigra::Rect2D &rect, double scale, unsigned nPoints, std::multimap< double, vigra::Diff2D > &points)
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
helper for OpenMP
void fill_set(_Container &c, typename _Container::key_type begin, typename _Container::key_type end)
Definition stl_utils.h:81
void SaveToImgs()
double m_center[2]
double m_dist[3][3]
double m_hfov[3]
HuginBase::PanoramaData & m_pano
double m_shift[2]
const HuginBase::OptimizeVector & m_optvars
void FromX(double *x)
copy new values from x to internal optimization variables
OptimData(HuginBase::PanoramaData &pano, const HuginBase::OptimizeVector &optvars, double mEstimatorSigma, int maxIter)
double huberSigma
void LoadFromImgs()
std::vector< double * > m_mapping
void ToX(double *x)
copy internal optimization variables into x
std::set< std::string > optvars
std::string alignedPrefix
std::string red_name
unsigned grid
std::string green_name
std::string ptoOutputFile
std::string ptoFile
std::string basename
std::string blue_name
Maximum of correlation, position and value.
Definition Correlation.h:57
hugin_utils::FDiff2D maxpos
Definition Correlation.h:64
void optimize_new(HuginBase::PanoramaData &pano)
Parameters g_param
static int ptProgress(int command, char *argument)
int main(int argc, char *argv[])
#define DEFAULT_OPTIMISATION_PARAMETER
static int ptinfoDlg(int command, char *argument)
void get_optvars(HuginBase::OptimizeVector &_retval)
int main2(HuginBase::Panorama &pano)
int optimize_old(HuginBase::Panorama &pano)
int processPTO(const char *filename)
void createCtrlPoints(HuginBase::Panorama &pano, const ImageType &img, int imgRedNr, int imgGreenNr, int imgBlueNr, double scale, int nPoints, unsigned grid)
void resetValues(HuginBase::Panorama &pano)
void print_result(HuginBase::Panorama &pano)
void optGetError(double *p, double *x, int m, int n, void *data)
std::multimap< double, vigra::Diff2D > MapPoints
int optVis(double *p, double *x, int m, int n, int iter, double sqerror, void *data)
static hugin_omp::Lock lock
double weightHuber(double x, double sigma)
int processImg(const char *filename)
std::vector< deghosting::BImagePtr > threshold(const std::vector< deghosting::FImagePtr > &inputImages, const double threshold, const uint16_t flags)
Threshold function used for creating alpha masks for images.
Definition threshold.h:41
functions to manage ROI's