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::cout << 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         { "output", required_argument, NULL, 'o'},
00089         { "help", no_argument, NULL, 'h' },
00090         0
00091     };
00092     std::string output;
00093     bool doPairwise = false;
00094     bool doAutoOpt = false;
00095     bool doNormalOpt = false;
00096     bool doLevel = false;
00097     bool chooseProj = false;
00098     bool quiet = false;
00099     bool doPhotometric = false;
00100     double hfov = 0.0;
00101     while ((c = getopt_long(argc, argv, optstring, longOptions, nullptr)) != -1)
00102     {
00103         switch (c)
00104         {
00105             case 'o':
00106                 output = optarg;
00107                 break;
00108             case 'h':
00109                 usage(hugin_utils::stripPath(argv[0]).c_str());
00110                 return 0;
00111             case 'p':
00112                 doPairwise = true;
00113                 break;
00114             case 'a':
00115                 doAutoOpt = true;
00116                 break;
00117             case 'n':
00118                 doNormalOpt = true;
00119                 break;
00120             case 'l':
00121                 doLevel = true;
00122                 break;
00123             case 's':
00124                 chooseProj = true;
00125                 break;
00126             case 'q':
00127                 quiet = true;
00128                 break;
00129             case 'v':
00130                 hfov = atof(optarg);
00131                 break;
00132             case 'm':
00133                 doPhotometric = true;
00134                 break;
00135             case ':':
00136             case '?':
00137                 // missing argument or invalid switch
00138                 return 1;
00139                 break;
00140             default:
00141                 // this should not happen
00142                 abort ();
00143         }
00144     }
00145 
00146     if (argc - optind != 1)
00147     {
00148         if (argc - optind < 1)
00149         {
00150             std::cerr << hugin_utils::stripPath(argv[0]) << ": No project file given." << std::endl;
00151         }
00152         else
00153         {
00154             std::cerr << hugin_utils::stripPath(argv[0]) << ": Only one project file expected." << std::endl;
00155         };
00156         return 1;
00157     }
00158 
00159     const char* scriptFile = argv[optind];
00160 
00161     HuginBase::Panorama pano;
00162     if (scriptFile[0] == '-')
00163     {
00164         AppBase::DocumentData::ReadWriteError err = pano.readData(std::cin);
00165         if (err != AppBase::DocumentData::SUCCESSFUL)
00166         {
00167             std::cerr << "error while reading script file from stdin." << std::endl
00168                 << "DocumentData::ReadWriteError code: " << err << std::endl;
00169             return 1;
00170         }
00171     }
00172     else
00173     {
00174         std::ifstream prjfile(scriptFile);
00175         if (!prjfile.good())
00176         {
00177             std::cerr << "could not open script : " << scriptFile << std::endl;
00178             return 1;
00179         }
00180         pano.setFilePrefix(hugin_utils::getPathPrefix(scriptFile));
00181         AppBase::DocumentData::ReadWriteError err = pano.readData(prjfile);
00182         if (err != AppBase::DocumentData::SUCCESSFUL)
00183         {
00184             std::cerr << "error while parsing panos tool script: " << scriptFile << std::endl
00185                 << "DocumentData::ReadWriteError code: " << err << std::endl;
00186             return 1;
00187         }
00188     }
00189 
00190     if (pano.getNrOfImages() == 0)
00191     {
00192         std::cerr << "Panorama should consist of at least one image" << std::endl;
00193         return 1;
00194     }
00195 
00196     // for bad HFOV (from autopano-SIFT)
00197     for (unsigned i=0; i < pano.getNrOfImages(); i++)
00198     {
00199         HuginBase::SrcPanoImage img = pano.getSrcImage(i);
00200         if (img.getProjection() == HuginBase::SrcPanoImage::RECTILINEAR
00201                 && img.getHFOV() >= 180)
00202         {
00203             // something is wrong here, try to read from exif data
00204             std::cerr << "HFOV of image " << img.getFilename() << " invalid, trying to read EXIF tags" << std::endl;
00205             img.readEXIF();
00206             bool ok = img.applyEXIFValues(false);
00207             if (! ok)
00208             {
00209                 if (hfov)
00210                 {
00211                     img.setHFOV(hfov);
00212                 }
00213                 else
00214                 {
00215                     std::cerr << "EXIF reading failed, please specify HFOV with -v" << std::endl;
00216                     return 1;
00217                 }
00218             }
00219             pano.setSrcImage(i, img);
00220         }
00221     }
00222 
00223     if(pano.getNrOfCtrlPoints()==0 && (doPairwise || doAutoOpt || doNormalOpt))
00224     {
00225         std::cerr << "Panorama have to have control points to optimise positions" << std::endl;
00226         return 1;
00227     };
00228     if (doPairwise && ! doAutoOpt)
00229     {
00230         // do pairwise optimisation
00231         HuginBase::AutoOptimise::autoOptimise(pano);
00232 
00233         // do global optimisation
00234         if (!quiet)
00235         {
00236             std::cerr << "*** Pairwise position optimisation" << std::endl;
00237         }
00238         HuginBase::PTools::optimize(pano);
00239     }
00240     else if (doAutoOpt)
00241     {
00242         if (!quiet)
00243         {
00244             std::cerr << "*** Adaptive geometric optimisation" << std::endl;
00245         }
00246         HuginBase::SmartOptimise::smartOptimize(pano);
00247     }
00248     else if (doNormalOpt)
00249     {
00250         if (!quiet)
00251         {
00252             std::cerr << "*** Optimising parameters specified in PTO file" << std::endl;
00253         }
00254         HuginBase::PTools::optimize(pano);
00255     }
00256     else
00257     {
00258         if (!quiet)
00259         {
00260             std::cerr << "*** Geometric parameters not optimized" << std::endl;
00261         }
00262     }
00263 
00264     if (doLevel)
00265     {
00266         bool hasVerticalLines=false;
00267         HuginBase::CPVector allCP=pano.getCtrlPoints();
00268         if(allCP.size()>0 && (doPairwise || doAutoOpt || doNormalOpt))
00269         {
00270             for(size_t i=0; i<allCP.size() && !hasVerticalLines; i++)
00271             {
00272                 hasVerticalLines=(allCP[i].mode==HuginBase::ControlPoint::X);
00273             };
00274         };
00275         // straighten only if there are no vertical control points
00276         if(hasVerticalLines)
00277         {
00278             std::cout << "Skipping automatic leveling because of existing vertical control points." << std::endl;
00279         }
00280         else
00281         {
00282             HuginBase::StraightenPanorama(pano).run();
00283             HuginBase::CenterHorizontally(pano).run();
00284         };
00285     }
00286 
00287     if (chooseProj)
00288     {
00289         HuginBase::PanoramaOptions opts = pano.getOptions();
00290         HuginBase::CalculateFitPanorama fitPano(pano);
00291         fitPano.run();
00292         opts.setHFOV(fitPano.getResultHorizontalFOV());
00293         opts.setHeight(hugin_utils::roundi(fitPano.getResultHeight()));
00294         const double vfov = opts.getVFOV();
00295         const double hfov = opts.getHFOV();
00296         // avoid perspective projection if field of view > 100 deg
00297         const double mf = 100;
00298         bool changedProjection=false;
00299         if (pano.getNrOfImages() == 1)
00300         {
00301             // special case for single image projects
00302             switch (pano.getImage(0).getProjection())
00303             {
00304                 case HuginBase::SrcPanoImage::RECTILINEAR:
00305                     // single rectilinear image, keep rectilinear projection
00306                     if (opts.getProjection() != HuginBase::PanoramaOptions::RECTILINEAR)
00307                     {
00308                         opts.setProjection(HuginBase::PanoramaOptions::RECTILINEAR);
00309                         changedProjection = true;
00310                     };
00311                     break;
00312                 default:
00313                     if (vfov < mf)
00314                     {
00315                         // small vfov, use cylindrical
00316                         if (opts.getProjection() != HuginBase::PanoramaOptions::CYLINDRICAL)
00317                         {
00318                             opts.setProjection(HuginBase::PanoramaOptions::CYLINDRICAL);
00319                             changedProjection = true;
00320                         };
00321                     }
00322                     else
00323                     {
00324                         // otherwise go to equirectangular
00325                         if (opts.getProjection() != HuginBase::PanoramaOptions::EQUIRECTANGULAR)
00326                         {
00327                             opts.setProjection(HuginBase::PanoramaOptions::EQUIRECTANGULAR);
00328                             changedProjection = true;
00329                         };
00330                     };
00331                     break;
00332             };
00333         }
00334         else
00335         {
00336             if (vfov < mf)
00337             {
00338                 // cylindrical or rectilinear
00339                 if (hfov < mf)
00340                 {
00341                     if (opts.getProjection() != HuginBase::PanoramaOptions::RECTILINEAR)
00342                     {
00343                         opts.setProjection(HuginBase::PanoramaOptions::RECTILINEAR);
00344                         changedProjection = true;
00345                     };
00346                 }
00347                 else
00348                 {
00349                     if (opts.getProjection() != HuginBase::PanoramaOptions::CYLINDRICAL)
00350                     {
00351                         opts.setProjection(HuginBase::PanoramaOptions::CYLINDRICAL);
00352                         changedProjection = true;
00353                     };
00354                 };
00355             }
00356             else
00357             {
00358                 // vfov > 100, use equirectangular projection
00359                 if (opts.getProjection() != HuginBase::PanoramaOptions::EQUIRECTANGULAR)
00360                 {
00361                     opts.setProjection(HuginBase::PanoramaOptions::EQUIRECTANGULAR);
00362                     changedProjection = true;
00363                 };
00364             };
00365         };
00366         pano.setOptions(opts);
00367         // the projection could be changed, calculate fit again
00368         if(changedProjection)
00369         {
00370             HuginBase::CalculateFitPanorama fitPano2(pano);
00371             fitPano2.run();
00372             opts.setHFOV(fitPano2.getResultHorizontalFOV());
00373             opts.setHeight(hugin_utils::roundi(fitPano2.getResultHeight()));
00374             pano.setOptions(opts);
00375         };
00376 
00377         // downscale pano a little
00378         const double sizeFactor = 0.7;
00379         const double w = HuginBase::CalculateOptimalScale::calcOptimalScale(pano);
00380         opts.setWidth(hugin_utils::roundi(opts.getWidth()*w*sizeFactor), true);
00381         pano.setOptions(opts);
00382     }
00383 
00384     if(doPhotometric)
00385     {
00386         // photometric estimation
00387         HuginBase::PanoramaOptions opts = pano.getOptions();
00388         int nPoints = 200;
00389         nPoints = nPoints * pano.getNrOfImages();
00390 
00391         std::vector<vigra_ext::PointPairRGB> points;
00392         AppBase::ProgressDisplay* progressDisplay;
00393         if(!quiet)
00394         {
00395             progressDisplay=new AppBase::StreamProgressDisplay(std::cout);
00396         }
00397         else
00398         {
00399             progressDisplay=new AppBase::DummyProgressDisplay();
00400         }
00401         float imageStepSize;
00402         try
00403         {
00404             loadImgsAndExtractPoints(pano, nPoints, 3, true, *progressDisplay, points, !quiet, imageStepSize);
00405         }
00406         catch (std::exception& e)
00407         {
00408             std::cerr << "caught exception: " << e.what() << std::endl;
00409             return 1;
00410         };
00411         if(!quiet)
00412         {
00413             std::cout << "\rSelected " << points.size() << " points" << std::endl;
00414         }
00415 
00416         if (points.size() == 0)
00417         {
00418             std::cerr << "Error: no overlapping points found, exiting" << std::endl;
00419             return 1;
00420         }
00421 
00422         progressDisplay->setMessage("Photometric Optimization");
00423         // first, ensure that vignetting and response coefficients are linked
00424         const HuginBase::ImageVariableGroup::ImageVariableEnum vars[] =
00425         {
00426             HuginBase::ImageVariableGroup::IVE_EMoRParams,
00427             HuginBase::ImageVariableGroup::IVE_ResponseType,
00428             HuginBase::ImageVariableGroup::IVE_VigCorrMode,
00429             HuginBase::ImageVariableGroup::IVE_RadialVigCorrCoeff,
00430             HuginBase::ImageVariableGroup::IVE_RadialVigCorrCenterShift
00431         };
00432         HuginBase::StandardImageVariableGroups variable_groups(pano);
00433         HuginBase::ImageVariableGroup& lenses = variable_groups.getLenses();
00434         for (size_t i = 0; i < lenses.getNumberOfParts(); i++)
00435         {
00436             std::set<HuginBase::ImageVariableGroup::ImageVariableEnum> links_needed;
00437             links_needed.clear();
00438             for (int v = 0; v < 5; v++)
00439             {
00440                 if (!lenses.getVarLinkedInPart(vars[v], i))
00441                 {
00442                     links_needed.insert(vars[v]);
00443                 }
00444             };
00445             if (!links_needed.empty())
00446             {
00447                 std::set<HuginBase::ImageVariableGroup::ImageVariableEnum>::iterator it;
00448                 for (it = links_needed.begin(); it != links_needed.end(); ++it)
00449                 {
00450                     lenses.linkVariablePart(*it, i);
00451                 }
00452             }
00453         }
00454 
00455         HuginBase::SmartPhotometricOptimizer::PhotometricOptimizeMode optmode =
00456             HuginBase::SmartPhotometricOptimizer::OPT_PHOTOMETRIC_LDR_WB;
00457         if (opts.outputMode == HuginBase::PanoramaOptions::OUTPUT_HDR)
00458         {
00459             optmode = HuginBase::SmartPhotometricOptimizer::OPT_PHOTOMETRIC_HDR;
00460         }
00461         HuginBase::SmartPhotometricOptimizer photoOpt(pano, progressDisplay, pano.getOptimizeVector(), points, imageStepSize, optmode);
00462         photoOpt.run();
00463 
00464         // calculate the mean exposure.
00465         opts.outputExposureValue = HuginBase::CalculateMeanExposure::calcMeanExposure(pano);
00466         pano.setOptions(opts);
00467         progressDisplay->taskFinished();
00468         delete progressDisplay;
00469     };
00470 
00471     // write result
00472     HuginBase::OptimizeVector optvec = pano.getOptimizeVector();
00473     HuginBase::UIntSet imgs;
00474     fill_set(imgs,0, pano.getNrOfImages()-1);
00475     if (output != "")
00476     {
00477         std::ofstream of(output.c_str());
00478         pano.printPanoramaScript(of, optvec, pano.getOptions(), imgs, false, hugin_utils::getPathPrefix(scriptFile));
00479     }
00480     else
00481     {
00482         pano.printPanoramaScript(std::cout, optvec, pano.getOptions(), imgs, false, hugin_utils::getPathPrefix(scriptFile));
00483     }
00484     return 0;
00485 }

Generated on 16 Jan 2018 for Hugintrunk by  doxygen 1.4.7