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

Generated on 4 May 2016 for Hugintrunk by  doxygen 1.4.7