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