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

Generated on 28 Jul 2015 for Hugintrunk by  doxygen 1.4.7