autooptimiser.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 -*-
00002 
00028 #include <hugin_config.h>
00029 
00030 #include <fstream>
00031 #include <sstream>
00032 #include <getopt.h>
00033 
00034 #include <hugin_basic.h>
00035 #include <hugin_utils/stl_utils.h>
00036 #include <appbase/ProgressDisplay.h>
00037 #include <algorithms/optimizer/PTOptimizer.h>
00038 #include <algorithms/nona/CenterHorizontally.h>
00039 #include <algorithms/basic/StraightenPanorama.h>
00040 #include <algorithms/basic/CalculateMeanExposure.h>
00041 #include <algorithms/nona/FitPanorama.h>
00042 #include <algorithms/basic/CalculateOptimalScale.h>
00043 #include <algorithms/optimizer/PhotometricOptimizer.h>
00044 #include <panodata/ImageVariableGroup.h>
00045 #include <panodata/StandardImageVariableGroups.h>
00046 #include "ExtractPoints.h"
00047 
00048 static void usage(const char* name)
00049 {
00050     std::cerr << name << ": optimize image positions" << std::endl
00051         << "autooptimiser version " << hugin_utils::GetHuginVersion() << std::endl
00052         << std::endl
00053         << "Usage:  " << name << " [options] input.pto" << std::endl
00054         << "   To read a project from stdio, specify - as input file." << std::endl
00055         << std::endl
00056         << "  Options:" << std::endl
00057         << "     -o file.pto  output file. If omitted, stdout is used." << std::endl
00058         << std::endl
00059          << "    Optimisation options (if not specified, no optimisation takes place)" << std::endl
00060          << "     -a       auto align mode, includes various optimisation stages, depending" << std::endl
00061          << "               on the amount and distribution of the control points" << std::endl
00062          << "     -p       pairwise optimisation of yaw, pitch and roll, starting from" << std::endl
00063          << "              first image" << std::endl
00064          << "     -m       Optimise photometric parameters" << std::endl
00065          << "     -n       Optimize parameters specified in script file (like PTOptimizer)" << std::endl
00066          << std::endl
00067          << "    Postprocessing options:" << std::endl
00068          << "     -l       level horizon (works best for horizontal panos)" << std::endl
00069          << "     -s       automatically select a suitable output projection and size" << std::endl
00070          << "    Other options:" << std::endl
00071          << "     -q       quiet operation (no progress is reported)" << std::endl
00072          << "     -v HFOV  specify horizontal field of view of input images." << std::endl
00073          << "               Used if the .pto file contains invalid HFOV values" << std::endl
00074          << "               (autopano-SIFT writes .pto files with invalid HFOV)" << std::endl
00075          << std::endl
00076          << "   When using -a -l -m and -s options together, a similar operation to the \"Align\"" << std::endl
00077          << "    button in hugin is performed." << std::endl
00078          << std::endl;
00079 }
00080 
00081 int main(int argc, char* argv[])
00082 {
00083     // parse arguments
00084     const char* optstring = "alho:npqsv:m";
00085     int c;
00086     static struct option longOptions[] =
00087     {
00088         { "help", no_argument, NULL, 'h' },
00089         0
00090     };
00091     std::string output;
00092     bool doPairwise = false;
00093     bool doAutoOpt = false;
00094     bool doNormalOpt = false;
00095     bool doLevel = false;
00096     bool chooseProj = false;
00097     bool quiet = false;
00098     bool doPhotometric = false;
00099     double hfov = 0.0;
00100     while ((c = getopt_long(argc, argv, optstring, longOptions, nullptr)) != -1)
00101     {
00102         switch (c)
00103         {
00104             case 'o':
00105                 output = optarg;
00106                 break;
00107             case 'h':
00108                 usage(hugin_utils::stripPath(argv[0]).c_str());
00109                 return 0;
00110             case 'p':
00111                 doPairwise = true;
00112                 break;
00113             case 'a':
00114                 doAutoOpt = true;
00115                 break;
00116             case 'n':
00117                 doNormalOpt = true;
00118                 break;
00119             case 'l':
00120                 doLevel = true;
00121                 break;
00122             case 's':
00123                 chooseProj = true;
00124                 break;
00125             case 'q':
00126                 quiet = true;
00127                 break;
00128             case 'v':
00129                 hfov = atof(optarg);
00130                 break;
00131             case 'm':
00132                 doPhotometric = true;
00133                 break;
00134             case ':':
00135             case '?':
00136                 // missing argument or invalid switch
00137                 return 1;
00138                 break;
00139             default:
00140                 // this should not happen
00141                 abort ();
00142         }
00143     }
00144 
00145     if (argc - optind != 1)
00146     {
00147         if (argc - optind < 1)
00148         {
00149             std::cerr << hugin_utils::stripPath(argv[0]) << ": No project file given." << std::endl;
00150         }
00151         else
00152         {
00153             std::cerr << hugin_utils::stripPath(argv[0]) << ": Only one project file expected." << std::endl;
00154         };
00155         return 1;
00156     }
00157 
00158     const char* scriptFile = argv[optind];
00159 
00160     HuginBase::Panorama pano;
00161     if (scriptFile[0] == '-')
00162     {
00163         AppBase::DocumentData::ReadWriteError err = pano.readData(std::cin);
00164         if (err != AppBase::DocumentData::SUCCESSFUL)
00165         {
00166             std::cerr << "error while reading script file from stdin." << std::endl
00167                 << "DocumentData::ReadWriteError code: " << err << std::endl;
00168             return 1;
00169         }
00170     }
00171     else
00172     {
00173         std::ifstream prjfile(scriptFile);
00174         if (!prjfile.good())
00175         {
00176             std::cerr << "could not open script : " << scriptFile << std::endl;
00177             return 1;
00178         }
00179         pano.setFilePrefix(hugin_utils::getPathPrefix(scriptFile));
00180         AppBase::DocumentData::ReadWriteError err = pano.readData(prjfile);
00181         if (err != AppBase::DocumentData::SUCCESSFUL)
00182         {
00183             std::cerr << "error while parsing panos tool script: " << scriptFile << std::endl
00184                 << "DocumentData::ReadWriteError code: " << err << std::endl;
00185             return 1;
00186         }
00187     }
00188 
00189     if (pano.getNrOfImages() == 0)
00190     {
00191         std::cerr << "Panorama should consist of at least one image" << std::endl;
00192         return 1;
00193     }
00194 
00195     // for bad HFOV (from autopano-SIFT)
00196     for (unsigned i=0; i < pano.getNrOfImages(); i++)
00197     {
00198         HuginBase::SrcPanoImage img = pano.getSrcImage(i);
00199         if (img.getProjection() == HuginBase::SrcPanoImage::RECTILINEAR
00200                 && img.getHFOV() >= 180)
00201         {
00202             // something is wrong here, try to read from exif data
00203             std::cerr << "HFOV of image " << img.getFilename() << " invalid, trying to read EXIF tags" << std::endl;
00204             img.readEXIF();
00205             bool ok = img.applyEXIFValues(false);
00206             if (! ok)
00207             {
00208                 if (hfov)
00209                 {
00210                     img.setHFOV(hfov);
00211                 }
00212                 else
00213                 {
00214                     std::cerr << "EXIF reading failed, please specify HFOV with -v" << std::endl;
00215                     return 1;
00216                 }
00217             }
00218             pano.setSrcImage(i, img);
00219         }
00220     }
00221 
00222     if(pano.getNrOfCtrlPoints()==0 && (doPairwise || doAutoOpt || doNormalOpt))
00223     {
00224         std::cerr << "Panorama have to have control points to optimise positions" << std::endl;
00225         return 1;
00226     };
00227     if (doPairwise && ! doAutoOpt)
00228     {
00229         // do pairwise optimisation
00230         HuginBase::AutoOptimise::autoOptimise(pano);
00231 
00232         // do global optimisation
00233         if (!quiet)
00234         {
00235             std::cerr << "*** Pairwise position optimisation" << std::endl;
00236         }
00237         HuginBase::PTools::optimize(pano);
00238     }
00239     else if (doAutoOpt)
00240     {
00241         if (!quiet)
00242         {
00243             std::cerr << "*** Adaptive geometric optimisation" << std::endl;
00244         }
00245         HuginBase::SmartOptimise::smartOptimize(pano);
00246     }
00247     else if (doNormalOpt)
00248     {
00249         if (!quiet)
00250         {
00251             std::cerr << "*** Optimising parameters specified in PTO file" << std::endl;
00252         }
00253         HuginBase::PTools::optimize(pano);
00254     }
00255     else
00256     {
00257         if (!quiet)
00258         {
00259             std::cerr << "*** Geometric parameters not optimized" << std::endl;
00260         }
00261     }
00262 
00263     if (doLevel)
00264     {
00265         bool hasVerticalLines=false;
00266         HuginBase::CPVector allCP=pano.getCtrlPoints();
00267         if(allCP.size()>0 && (doPairwise || doAutoOpt || doNormalOpt))
00268         {
00269             for(size_t i=0; i<allCP.size() && !hasVerticalLines; i++)
00270             {
00271                 hasVerticalLines=(allCP[i].mode==HuginBase::ControlPoint::X);
00272             };
00273         };
00274         // straighten only if there are no vertical control points
00275         if(hasVerticalLines)
00276         {
00277             std::cout << "Skipping automatic leveling because of existing vertical control points." << std::endl;
00278         }
00279         else
00280         {
00281             HuginBase::StraightenPanorama(pano).run();
00282             HuginBase::CenterHorizontally(pano).run();
00283         };
00284     }
00285 
00286     if (chooseProj)
00287     {
00288         HuginBase::PanoramaOptions opts = pano.getOptions();
00289         HuginBase::CalculateFitPanorama fitPano(pano);
00290         fitPano.run();
00291         opts.setHFOV(fitPano.getResultHorizontalFOV());
00292         opts.setHeight(hugin_utils::roundi(fitPano.getResultHeight()));
00293         const double vfov = opts.getVFOV();
00294         const double hfov = opts.getHFOV();
00295         // avoid perspective projection if field of view > 100 deg
00296         const double mf = 100;
00297         bool changedProjection=false;
00298         if (vfov < mf)
00299         {
00300             // cylindrical or rectilinear
00301             if (hfov < mf)
00302             {
00303                 if (opts.getProjection() != HuginBase::PanoramaOptions::RECTILINEAR)
00304                 {
00305                     opts.setProjection(HuginBase::PanoramaOptions::RECTILINEAR);
00306                     changedProjection = true;
00307                 };
00308             }
00309             else
00310             {
00311                 if (opts.getProjection() != HuginBase::PanoramaOptions::CYLINDRICAL)
00312                 {
00313                     opts.setProjection(HuginBase::PanoramaOptions::CYLINDRICAL);
00314                     changedProjection = true;
00315                 };
00316             }
00317         }
00318         pano.setOptions(opts);
00319         // the projection could be changed, calculate fit again
00320         if(changedProjection)
00321         {
00322             HuginBase::CalculateFitPanorama fitPano2(pano);
00323             fitPano2.run();
00324             opts.setHFOV(fitPano2.getResultHorizontalFOV());
00325             opts.setHeight(hugin_utils::roundi(fitPano2.getResultHeight()));
00326             pano.setOptions(opts);
00327         };
00328 
00329         // downscale pano a little
00330         const double sizeFactor = 0.7;
00331         const double w = HuginBase::CalculateOptimalScale::calcOptimalScale(pano);
00332         opts.setWidth(hugin_utils::roundi(opts.getWidth()*w*sizeFactor), true);
00333         pano.setOptions(opts);
00334     }
00335 
00336     if(doPhotometric)
00337     {
00338         // photometric estimation
00339         HuginBase::PanoramaOptions opts = pano.getOptions();
00340         int nPoints = 200;
00341         nPoints = nPoints * pano.getNrOfImages();
00342 
00343         std::vector<vigra_ext::PointPairRGB> points;
00344         AppBase::ProgressDisplay* progressDisplay;
00345         if(!quiet)
00346         {
00347             progressDisplay=new AppBase::StreamProgressDisplay(std::cout);
00348         }
00349         else
00350         {
00351             progressDisplay=new AppBase::DummyProgressDisplay();
00352         }
00353         float imageStepSize;
00354         try
00355         {
00356             loadImgsAndExtractPoints(pano, nPoints, 3, true, *progressDisplay, points, !quiet, imageStepSize);
00357         }
00358         catch (std::exception& e)
00359         {
00360             std::cerr << "caught exception: " << e.what() << std::endl;
00361             return 1;
00362         };
00363         if(!quiet)
00364         {
00365             std::cout << "\rSelected " << points.size() << " points" << std::endl;
00366         }
00367 
00368         if (points.size() == 0)
00369         {
00370             std::cerr << "Error: no overlapping points found, exiting" << std::endl;
00371             return 1;
00372         }
00373 
00374         progressDisplay->setMessage("Photometric Optimization");
00375         // first, ensure that vignetting and response coefficients are linked
00376         const HuginBase::ImageVariableGroup::ImageVariableEnum vars[] =
00377         {
00378             HuginBase::ImageVariableGroup::IVE_EMoRParams,
00379             HuginBase::ImageVariableGroup::IVE_ResponseType,
00380             HuginBase::ImageVariableGroup::IVE_VigCorrMode,
00381             HuginBase::ImageVariableGroup::IVE_RadialVigCorrCoeff,
00382             HuginBase::ImageVariableGroup::IVE_RadialVigCorrCenterShift
00383         };
00384         HuginBase::StandardImageVariableGroups variable_groups(pano);
00385         HuginBase::ImageVariableGroup& lenses = variable_groups.getLenses();
00386         for (size_t i = 0; i < lenses.getNumberOfParts(); i++)
00387         {
00388             std::set<HuginBase::ImageVariableGroup::ImageVariableEnum> links_needed;
00389             links_needed.clear();
00390             for (int v = 0; v < 5; v++)
00391             {
00392                 if (!lenses.getVarLinkedInPart(vars[v], i))
00393                 {
00394                     links_needed.insert(vars[v]);
00395                 }
00396             };
00397             if (!links_needed.empty())
00398             {
00399                 std::set<HuginBase::ImageVariableGroup::ImageVariableEnum>::iterator it;
00400                 for (it = links_needed.begin(); it != links_needed.end(); ++it)
00401                 {
00402                     lenses.linkVariablePart(*it, i);
00403                 }
00404             }
00405         }
00406 
00407         HuginBase::SmartPhotometricOptimizer::PhotometricOptimizeMode optmode =
00408             HuginBase::SmartPhotometricOptimizer::OPT_PHOTOMETRIC_LDR_WB;
00409         if (opts.outputMode == HuginBase::PanoramaOptions::OUTPUT_HDR)
00410         {
00411             optmode = HuginBase::SmartPhotometricOptimizer::OPT_PHOTOMETRIC_HDR;
00412         }
00413         HuginBase::SmartPhotometricOptimizer photoOpt(pano, progressDisplay, pano.getOptimizeVector(), points, imageStepSize, optmode);
00414         photoOpt.run();
00415 
00416         // calculate the mean exposure.
00417         opts.outputExposureValue = HuginBase::CalculateMeanExposure::calcMeanExposure(pano);
00418         pano.setOptions(opts);
00419         progressDisplay->taskFinished();
00420         delete progressDisplay;
00421     };
00422 
00423     // write result
00424     HuginBase::OptimizeVector optvec = pano.getOptimizeVector();
00425     HuginBase::UIntSet imgs;
00426     fill_set(imgs,0, pano.getNrOfImages()-1);
00427     if (output != "")
00428     {
00429         std::ofstream of(output.c_str());
00430         pano.printPanoramaScript(of, optvec, pano.getOptions(), imgs, false, hugin_utils::getPathPrefix(scriptFile));
00431     }
00432     else
00433     {
00434         pano.printPanoramaScript(std::cout, optvec, pano.getOptions(), imgs, false, hugin_utils::getPathPrefix(scriptFile));
00435     }
00436     return 0;
00437 }

Generated on 9 Dec 2016 for Hugintrunk by  doxygen 1.4.7