align_image_stack.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 -*-
00002 
00027 #include <hugin_config.h>
00028 #include <hugin_version.h>
00029 #include <fstream>
00030 #include <sstream>
00031 
00032 #include <vigra/error.hxx>
00033 #include <vigra/impex.hxx>
00034 #include <vigra/cornerdetection.hxx>
00035 #include <vigra/localminmax.hxx>
00036 #include <hugin_utils/utils.h>
00037 
00038 #include <vigra_ext/Pyramid.h>
00039 #include <vigra_ext/Correlation.h>
00040 #include <vigra_ext/InterestPoints.h>
00041 
00042 #include <panodata/Panorama.h>
00043 #include <panotools/PanoToolsOptimizerWrapper.h>
00044 #include <panodata/StandardImageVariableGroups.h>
00045 #include <algorithms/optimizer/PTOptimizer.h>
00046 #include <nona/Stitcher.h>
00047 #include <algorithms/basic/CalculateOptimalROI.h>
00048 
00049 #ifdef WIN32
00050  #include <getopt.h>
00051 #else
00052  #include <unistd.h>
00053 #endif
00054 
00055 
00056 #include <tiff.h>
00057 
00058 using namespace vigra;
00059 using namespace HuginBase;
00060 using namespace AppBase;
00061 using namespace std;
00062 using namespace vigra_ext;
00063 using namespace HuginBase::PTools;
00064 using namespace HuginBase::Nona;
00065 
00066 int g_verbose = 0;
00067 
00068 static void usage(const char * name)
00069 {
00070     cerr << name << ": align overlapping images for HDR creation" << std::endl
00071          << "align_image_stack version " << DISPLAY_VERSION << std::endl
00072          << std::endl
00073          << "Usage: " << name  << " [options] input files" << std::endl
00074          << "Valid options are:" << std::endl
00075          << " Modes of operation:" << std::endl
00076          << "  -p file   Output .pto file (useful for debugging, or further refinement)" << std::endl
00077          << "  -a prefix align images, output as prefix_xxxx.tif" << std::endl
00078          << "  -o output merge images to HDR, generate output.hdr" << std::endl
00079          << " Modifiers" << std::endl
00080          << "  -v        Verbose, print progress messages.  Repeat for higher verbosity" << std::endl
00081          << "  -e        Assume input images are full frame fish eye (default: rectilinear)" << std::endl
00082          << "  -t num    Remove all control points with an error higher than num pixels (default: 3)" << std::endl
00083          << "  -f HFOV   approximate horizontal field of view of input images, use if EXIF info not complete" << std::endl
00084          << "  -m        Optimize field of view for all images, except for first." << std::endl
00085          << "             Useful for aligning focus stacks with slightly different magnification." << std::endl
00086          << "  -d        Optimize radial distortion for all images, except for first." << std::endl
00087          << "  -i        Optimize image center shift for all images, except for first." << std::endl
00088          << "  -x        Optimize X coordinate of the camera position." << std::endl
00089          << "  -y        Optimize Y coordinate of the camera position." << std::endl
00090          << "  -z        Optimize Z coordinate of the camera position." << std::endl
00091          << "             Useful for aligning more distorted images." << std::endl
00092          << "  -S        Assume stereo images - allow horizontal shift of control points." << std::endl
00093          << "  -A        Align stereo window - assumes -S." << std::endl
00094          << "  -P        Align stereo window with pop-out effect - assumes -S." << std::endl
00095          << "  -C        Auto crop the image to the area covered by all images." << std::endl
00096          << "  -c num    number of control points (per grid) to create between adjacent images (default: 8)" << std::endl
00097          << "  -l        Assume linear input files" << std::endl
00098          << "  -s scale  Scale down image by 2^scale (default: 1 [2x downsampling])" << std::endl
00099          << "  -g gsize  Break image into a rectangular grid (gsize x gsize) and attempt to find " << std::endl 
00100          << "             num control points in each section (default: 5 [5x5 grid] )" << std::endl
00101          << "  -h        Display help (this text)" << std::endl
00102          << std::endl;
00103 }
00104 
00105 
00106 #if 0
00107 template <class VALUETYPE>
00108 class InterestPointSelector
00109 {
00110 public:
00111 
00114     typedef VALUETYPE argument_type;
00115 
00118     typedef VALUETYPE result_type;
00119 
00122     typedef VALUETYPE value_type;
00123 
00124 
00125     InterestPointSelector(int nrPoints)
00126     {
00127         minResponse = 0;
00128         nPoints = nrPoints;
00129     }
00130 
00131     void operator()(argument_type const & resp)
00132     {
00133         //double resp = leftCornerResponse(x,y);
00134         if (resp > minResponse) {
00135             // add to point map
00136             points.insert(make_pair(resp,Diff2D(x,y)));
00137             // if we have reached the maximum
00138             // number of points, increase the threshold, to avoid
00139             // growing the points map too much.
00140             // extract more than nPoints, because some might be bad
00141             // and cannot be matched with the other image.
00142             if (points.size() > 5*nPoints) {
00143                 // remove the point with the lowest corner response.
00144                 leftCorners(points.begin()->second.x,points.begin()->second.y)=0;
00145                 points.erase(points.begin());
00146                 // use new threshold for next selection.
00147                 minResponse = points.begin()->first;
00148             }
00149         }
00150     }
00151 
00152     argument_type minResponse;
00153     std::multimap<argument_type, vigra::Diff2D> points;
00154     int nPoints;
00155 }
00156 
00157 #endif
00158 
00159 template <class ImageType>
00160 void createCtrlPoints(Panorama & pano, int img1, const ImageType & leftImg, const ImageType & leftImgOrig, int img2, const ImageType & rightImg, const ImageType & rightImgOrig, int pyrLevel, double scale, unsigned nPoints, unsigned grid, bool stereo = false)
00161 {
00162     typedef typename ImageType::value_type VT;
00164     // find interesting corners using harris corner detector
00165     typedef std::vector<std::multimap<double, vigra::Diff2D> > MapVector;
00166 
00167     if (stereo)
00168     {
00169         // add one vertical control point to keep the images aligned vertically
00170         ControlPoint p(img1, 0, 0, img2, 0, 0, ControlPoint::X);
00171         pano.addCtrlPoint(p);
00172     }
00173     std::vector<std::multimap<double, vigra::Diff2D> >points;
00174     if (g_verbose > 0) {
00175         std::cout << "Trying to find " << nPoints << " corners... ";
00176     }
00177 
00178     vigra_ext::findInterestPointsOnGrid(srcImageRange(leftImg, GreenAccessor<VT>()), scale, 5*nPoints, grid, points);
00179 
00180     if (stereo)
00181     {
00182         // add some additional control points around image edges
00183         // this is useful for better results - images are more distorted around edges
00184         // and also for stereoscopic window adjustment - it must be alligned according to
00185         // the nearest object which crosses the edge and these control points helps to find it.
00186         std::multimap<double, vigra::Diff2D> up;
00187         std::multimap<double, vigra::Diff2D> down;
00188         std::multimap<double, vigra::Diff2D> left;
00189         std::multimap<double, vigra::Diff2D> right;
00190         int xstep = leftImg.size().x / (nPoints + 1);
00191         int ystep = leftImg.size().y / (nPoints + 1);
00192         for (int k = 6; k >= 0; --k) 
00193             for (int j = 0; j < 2; ++j) 
00194                 for (int i = 0; i < nPoints; ++i) { 
00195                     up.insert(   std::make_pair(0, vigra::Diff2D(j * xstep / 2 + i * xstep ,    1 + k * 10)));
00196                     down.insert( std::make_pair(0, vigra::Diff2D(j * xstep / 2 + i * xstep ,    leftImg.size().y - 2 - k * 10)));
00197                     left.insert( std::make_pair(0, vigra::Diff2D(1 + k * 10,                    j * ystep / 2 + i * ystep)));
00198                     right.insert(std::make_pair(0, vigra::Diff2D(leftImg.size().x - 2 - k * 10, j * ystep / 2 + i * ystep)));
00199         }
00200         points.push_back(up);
00201         points.push_back(down);
00202         points.push_back(left);
00203         points.push_back(right);
00204     }  
00205 
00206     double scaleFactor = 1<<pyrLevel;
00207 
00208     for (MapVector::iterator mit = points.begin(); mit != points.end(); ++mit) {
00209 
00210         unsigned nGood = 0;
00211         unsigned nBad = 0;
00212         // loop over all points, starting with the highest corner score
00213         for (multimap<double, vigra::Diff2D>::reverse_iterator it = (*mit).rbegin();
00214              it != (*mit).rend();
00215              ++it)
00216         {
00217             if (nGood >= nPoints) {
00218                 // we have enough points, stop
00219                 break;
00220             }
00221 
00222             long templWidth = 20;
00223             long sWidth = 100;
00224             long sWidth2 = scaleFactor;
00225             double corrThresh = 0.9;
00226             //double curvThresh = 0.0;
00227 
00228             vigra_ext::CorrelationResult res;
00229 
00230             res = vigra_ext::PointFineTune(leftImg,
00231                                             (*it).second,
00232                                             templWidth,
00233                                             rightImg,
00234                                             (*it).second,
00235                                             sWidth
00236                                             );
00237             if (g_verbose > 2) {
00238                 cout << "I :" << (*it).second.x * scaleFactor << "," << (*it).second.y * scaleFactor << " -> "
00239                         << res.maxpos.x * scaleFactor << "," << res.maxpos.y * scaleFactor << ":  corr coeff: " <<  res.maxi
00240                         << " curv:" <<  res.curv.x << " " << res.curv.y << std::endl;
00241             }
00242             if (res.maxi < corrThresh )
00243             {
00244                 nBad++;
00245                 DEBUG_DEBUG("low correlation: " << res.maxi << " curv: " << res.curv);
00246                 continue;
00247             }
00248             
00249             if (pyrLevel > 0)
00250             {
00251                 res = vigra_ext::PointFineTune(leftImgOrig,
00252                                             Diff2D((*it).second.x * scaleFactor, (*it).second.y * scaleFactor),
00253                                             templWidth,
00254                                             rightImgOrig,
00255                                             Diff2D(res.maxpos.x * scaleFactor, res.maxpos.y * scaleFactor),
00256                                             sWidth2
00257                                             );
00258 
00259                 if (g_verbose > 2) {
00260                     cout << "II>" << (*it).second.x * scaleFactor << "," << (*it).second.y * scaleFactor << " -> "
00261                             << res.maxpos.x << "," << res.maxpos.y << ":  corr coeff: " <<  res.maxi
00262                             << " curv:" <<  res.curv.x << " " << res.curv.y << std::endl;
00263                 }
00264                 if (res.maxi < corrThresh )
00265                 {
00266                     nBad++;
00267                     DEBUG_DEBUG("low correlation in pass 2: " << res.maxi << " curv: " << res.curv);
00268                     continue;
00269                 }
00270             }
00271             
00272             nGood++;
00273             // add control point
00274             ControlPoint p(img1, (*it).second.x * scaleFactor,
00275                             (*it).second.y * scaleFactor,
00276                             img2, res.maxpos.x,
00277                             res.maxpos.y,
00278                             stereo ? ControlPoint::Y : ControlPoint::X_Y);
00279             pano.addCtrlPoint(p);
00280             
00281         }
00282         if (g_verbose > 0) {
00283             cout << "Number of good matches: " << nGood << ", bad matches: " << nBad << std::endl;
00284         }
00285     }
00286 };
00287 
00288 void alignStereoWindow(Panorama & pano, bool pop_out)
00289 {
00290     CPVector cps = pano.getCtrlPoints();
00291     std::vector<PTools::Transform *> transTable(pano.getNrOfImages());
00292 
00293     std::vector<int> max_i(pano.getNrOfImages() - 1, -1); // index of a point with biggest shift
00294     std::vector<int> max_i_b(pano.getNrOfImages() - 1, -1); // the same as above, only border points considered
00295     std::vector<double> max_dif(pano.getNrOfImages() - 1, -1000000000); // value of the shift
00296     std::vector<double> max_dif_b(pano.getNrOfImages() - 1, -1000000000); // the same as above, only border points considered
00297 
00298     for (int i=0; i < pano.getNrOfImages(); i++)
00299     {
00300         transTable[i] = new PTools::Transform();
00301         transTable[i]->createInvTransform(pano.getImage(i), pano.getOptions());
00302     }
00303 
00304     double rbs = 0.1; // relative border size
00305     
00306     for (int i=0; i < (int)cps.size(); i++) {
00307         if (cps[i].mode == ControlPoint::X) {
00308             if (max_i[cps[i].image1Nr] < 0) // first control point for given pair
00309                 max_i[cps[i].image1Nr] = i; // use it as a fallback in case on other points exist
00310             continue;
00311         }
00312         
00313         vigra::Size2D size1 = pano.getImage(cps[i].image1Nr).getSize();
00314         vigra::Size2D size2 = pano.getImage(cps[i].image2Nr).getSize();
00315         
00316         vigra::Rect2D rect1(size1);
00317         vigra::Rect2D rect2(size2);
00318         
00319         rect1.addBorder(-size1.width() * rbs, -size1.height() * rbs);
00320         rect2.addBorder(-size2.width() * rbs, -size2.height() * rbs);
00321         
00322 
00323         double xt1, yt1, xt2, yt2; 
00324         if(!transTable[cps[i].image1Nr]->transformImgCoord(xt1, yt1, cps[i].x1, cps[i].y1)) continue;        
00325         if(!transTable[cps[i].image2Nr]->transformImgCoord(xt2, yt2, cps[i].x2, cps[i].y2)) continue;        
00326 
00327         double dif = xt2 - xt1;
00328         if (dif > max_dif[cps[i].image1Nr]) {
00329             max_dif[cps[i].image1Nr] = dif;
00330             max_i[cps[i].image1Nr] = i;
00331         }
00332 
00333         if (!(rect1.contains(Point2D(cps[i].x1, cps[i].y1)) &&
00334             rect2.contains(Point2D(cps[i].x2, cps[i].y2)))) {
00335             // the same for border points only
00336             if (dif > max_dif_b[cps[i].image1Nr]) {
00337                 max_dif_b[cps[i].image1Nr] = dif;
00338                 max_i_b[cps[i].image1Nr] = i;
00339             }
00340         }
00341     }
00342 
00343     for (int i=0; i < pano.getNrOfImages(); i++)
00344     {
00345         delete transTable[i];
00346     }
00347     
00348     for (int i=0; i < (int)max_i.size(); i++) {
00349         if (pop_out && (max_i_b[i] >= 0)) // check points at border
00350             cps[max_i_b[i]].mode = ControlPoint::X_Y;
00351         else if (max_i[i] >= 0) // no points at border - use any point
00352             cps[max_i[i]].mode = ControlPoint::X_Y;
00353         else {
00354             //no points at all - should not happen
00355         }
00356         
00357     }
00358 
00359     CPVector newCPs;
00360     for (int i=0; i < (int)cps.size(); i++) {
00361         if (cps[i].mode != ControlPoint::X) { // remove the vertical control lines, X_Y points replaces them
00362             newCPs.push_back(cps[i]);
00363         }
00364     }
00365 
00366     pano.setCtrlPoints(newCPs);
00367 }
00368 
00369 void autoCrop(Panorama & pano)
00370 {
00371     CalculateOptimalROI cropPano(pano, true);
00372     cropPano.run();
00373         
00374     vigra::Rect2D roi=cropPano.getResultOptimalROI();
00375     //set the ROI - fail if the right/bottom is zero, meaning all zero
00376     if(roi.right() != 0 && roi.bottom() != 0)
00377     {
00378         PanoramaOptions opt = pano.getOptions();
00379         opt.setROI(roi);
00380         pano.setOptions(opt);
00381         cout << "Set crop size to " << roi.left() << "," << roi.top() << "," << roi.right() << "," << roi.bottom() << endl;
00382     }
00383     else {
00384         cout << "Could not find best crop rectangle for image" << endl;
00385     };
00386 }
00387 
00388 struct Parameters
00389 {
00390     Parameters()
00391     {
00392         cpErrorThreshold = 3;
00393         nPoints = 8;
00394         grid = 5;
00395         hfov = 0;
00396         pyrLevel = 1;
00397         linear = false;   // Assume linear input files if true
00398         optHFOV = false;
00399         optDistortion = false;
00400         optCenter = false;
00401         optX = false;
00402         optY = false;
00403         optZ = false;
00404         stereo = false;
00405         stereo_window = false;
00406         pop_out = false;
00407         crop = false;
00408         fisheye = false;
00409     }
00410 
00411     double cpErrorThreshold;
00412     int nPoints;
00413     int grid;           // Partition images into grid x grid subregions, each with npoints
00414     double hfov;
00415     bool linear;
00416     bool optHFOV;
00417     bool optDistortion;
00418     bool optCenter;
00419     bool optX;
00420     bool optY;
00421     bool optZ;
00422     bool fisheye;
00423     bool stereo;
00424     bool stereo_window;
00425     bool pop_out;
00426     bool crop;
00427     int pyrLevel;
00428     std::string alignedPrefix;
00429     std::string ptoFile;
00430     std::string hdrFile;
00431     string basename;
00432 };
00433 
00434 template <class PixelType>
00435 int main2(std::vector<std::string> files, Parameters param)
00436 {
00437     typedef vigra::BasicImage<PixelType> ImageType;
00438     try {
00439         // load first image
00440         vigra::ImageImportInfo firstImgInfo(files[0].c_str());
00441 
00442         // original size
00443         ImageType * leftImgOrig = new ImageType(firstImgInfo.size());
00444         // rescale image
00445         ImageType * leftImg = new ImageType();
00446         {
00447             if(firstImgInfo.numExtraBands() == 1) {
00448                 vigra::BImage alpha(firstImgInfo.size());
00449                 vigra::importImageAlpha(firstImgInfo, destImage(*leftImgOrig), destImage(alpha));
00450             } else if (firstImgInfo.numExtraBands() == 0) {
00451                 vigra::importImage(firstImgInfo, destImage(*leftImgOrig));
00452             } else {
00453                 vigra_fail("Images with multiple extra (alpha) channels not supported");
00454             }
00455             reduceNTimes(*leftImgOrig, *leftImg, param.pyrLevel);
00456         }
00457 
00458 
00459         Panorama pano;
00460         Lens l;
00461 
00462         // add the first image.to the panorama object
00463         // default settings
00464         double focalLength = 50;
00465         double cropFactor = 0;
00466         
00467         SrcPanoImage srcImg;
00468         srcImg.setFilename(files[0]);
00469         
00470         if (param.fisheye) {
00471             srcImg.setProjection(SrcPanoImage::FULL_FRAME_FISHEYE);
00472         }
00473         srcImg.readEXIF(focalLength, cropFactor, true, true);
00474         // disable autorotate
00475         srcImg.setRoll(0);
00476         if (srcImg.getSize().x == 0 || srcImg.getSize().y == 0) {
00477             cerr << "Could not decode image: " << files[0] << "Unsupported image file format";
00478             return 1;
00479         }
00480 
00481         // use hfov specified by user.
00482         if (param.hfov > 0) {
00483             srcImg.setHFOV(param.hfov);
00484         } else if (cropFactor == 0) {
00485             // could not read HFOV, assuming default: 50
00486             srcImg.setHFOV(50);
00487         }
00488         
00489         if (param.linear) {
00490             srcImg.setResponseType(SrcPanoImage::RESPONSE_LINEAR);
00491             if (g_verbose>0) {
00492                 cout << "Using linear response" << std::endl;
00493             }
00494         }
00495         
00496         pano.addImage(srcImg);
00497         
00498         // setup output to be exactly similar to input image
00499         PanoramaOptions opts;
00500 
00501         if (param.fisheye) {
00502             opts.setProjection(PanoramaOptions::FULL_FRAME_FISHEYE);
00503         } else {
00504             opts.setProjection(PanoramaOptions::RECTILINEAR);
00505         }
00506         opts.setHFOV(srcImg.getHFOV(), false);
00507 
00508         if (srcImg.getRoll() == 0.0 || srcImg.getRoll() == 180.0) {
00509             opts.setWidth(srcImg.getSize().x, false);
00510             opts.setHeight(srcImg.getSize().y);
00511         } else {
00512             opts.setWidth(srcImg.getSize().y, false);
00513             opts.setHeight(srcImg.getSize().x);
00514         }
00515             // output to tiff format
00516         opts.outputFormat = PanoramaOptions::TIFF_m;
00517         opts.tiff_saveROI = false;
00518         // m estimator, to be more robust against points on moving objects
00519         opts.huberSigma = 2;
00520         pano.setOptions(opts);
00521 
00522         // variables that should be optimized
00523         // optimize nothing in the first image
00524         OptimizeVector optvars(1);
00525 
00526         ImageType * rightImg = new ImageType(leftImg->size());
00527         ImageType * rightImgOrig = new ImageType(leftImgOrig->size());
00528         StandardImageVariableGroups variable_groups(pano);
00529 
00530         // loop to add images and control points between them.
00531         for (int i = 1; i < (int) files.size(); i++) {
00532             if (g_verbose > 0) {
00533                 cout << "Creating control points between " << files[i-1] << " and " << files[i] << std::endl;
00534             }
00535             // add next image.
00536             srcImg.setFilename(files[i]);
00537             srcImg.readEXIF(focalLength, cropFactor, true, true);
00538             if (srcImg.getSize().x == 0 || srcImg.getSize().y == 0) {
00539                 cerr << "Could not decode image: " << files[i] << "Unsupported image file format";
00540                 return 1;
00541             }
00542             if (param.hfov > 0) {
00543                 srcImg.setHFOV(param.hfov);
00544             } else if (cropFactor == 0) {
00545                 // could not read HFOV, assuming default: 50
00546                 srcImg.setHFOV(50);
00547             }
00548             
00549             int imgNr = pano.addImage(srcImg);
00550             variable_groups.update();
00551             // each image shares the same lens.
00552             variable_groups.getLenses().switchParts(imgNr, 0);
00553             // unlink HFOV?
00554             if (param.optHFOV) {
00555                 pano.unlinkImageVariableHFOV(0);
00556             }
00557             if (param.optDistortion) {
00558                 pano.unlinkImageVariableRadialDistortion(0);
00559             }
00560             if (param.optCenter) {
00561                 pano.unlinkImageVariableRadialDistortionCenterShift(0);
00562             }
00563             
00564             // All images are in the same stack: Link the stack variable.
00565             pano.linkImageVariableStack(imgNr, 0);
00566             
00567             // load the actual image data of the next image
00568             vigra::ImageImportInfo nextImgInfo(files[i].c_str());
00569             assert(nextImgInfo.size() == firstImgInfo.size());
00570             {
00571                 if (nextImgInfo.numExtraBands() == 1) {
00572                     vigra::BImage alpha(nextImgInfo.size());
00573                     vigra::importImageAlpha(nextImgInfo, destImage(*rightImgOrig), destImage(alpha));
00574                 } else if (nextImgInfo.numExtraBands() == 0) {
00575                     vigra::importImage(nextImgInfo, destImage(*rightImgOrig));
00576                 } else {
00577                     vigra_fail("Images with multiple extra (alpha) channels not supported");
00578                 }
00579                 reduceNTimes(*rightImgOrig, *rightImg, param.pyrLevel);
00580             }
00581 
00582             // add control points.
00583             // work on smaller images
00584             // TODO: or use a fast interest point operator.
00585             createCtrlPoints(pano, i-1, *leftImg, *leftImgOrig, i, *rightImg, *rightImgOrig, param.pyrLevel, 2, param.nPoints, param.grid, param.stereo);
00586 
00587             // swap images;
00588             delete leftImg;
00589             delete leftImgOrig;
00590             leftImg = rightImg;
00591             leftImgOrig = rightImgOrig;
00592             rightImg = new ImageType(leftImg->size());
00593             rightImgOrig = new ImageType(leftImgOrig->size());
00594 
00595             // optimize yaw, roll, pitch
00596             std::set<std::string> vars;
00597             vars.insert("y");
00598             vars.insert("p");
00599             vars.insert("r");
00600             if (param.optHFOV) {
00601                 vars.insert("v");
00602             }
00603             if (param.optDistortion) {
00604                 vars.insert("a");
00605                 vars.insert("b");
00606                 vars.insert("c");
00607             }
00608             if (param.optCenter) {
00609                 vars.insert("d");
00610                 vars.insert("e");
00611             }
00612             if (param.optX) {
00613                 vars.insert("TrX");
00614             }
00615             if (param.optY) {
00616                 vars.insert("TrY");
00617             }
00618             if (param.optZ) {
00619                 vars.insert("TrZ");
00620             }
00621             optvars.push_back(vars);
00622         }
00623         delete leftImg;
00624         delete rightImg;
00625         delete leftImgOrig;
00626         delete rightImgOrig;
00627 
00628         // optimize everything.
00629         pano.setOptimizeVector(optvars);
00630         bool optimizeError = false;
00631         optimizeError = (PTools::optimize(pano) > 0);
00632 
00633         // need to do some basic outlier pruning.
00634         // remove all points with error higher than a specified threshold
00635         if (param.cpErrorThreshold > 0) {
00636             CPVector cps = pano.getCtrlPoints();
00637             CPVector newCPs;
00638             for (int i=0; i < (int)cps.size(); i++) {
00639                 if (cps[i].error < param.cpErrorThreshold ||
00640                     cps[i].mode == ControlPoint::X) { // preserve the vertical control point for stereo alignment
00641                     newCPs.push_back(cps[i]);
00642                 }
00643             }
00644             if (g_verbose > 0) {
00645                 cout << "Ctrl points before pruning: " << cps.size() << ", after: " << newCPs.size() << std::endl;
00646             }
00647             pano.setCtrlPoints(newCPs);
00648             if (param.stereo_window) alignStereoWindow(pano, param.pop_out);
00649             // reoptimize
00650             optimizeError = (PTools::optimize(pano) > 0) ;
00651         }
00652         
00653         if (param.crop) autoCrop(pano);
00654 
00655         UIntSet imgs = pano.getActiveImages();
00656 
00657 
00658         if (optimizeError)
00659         {
00660             if (param.ptoFile.size() > 0) {
00661                 std::ofstream script(param.ptoFile.c_str());
00662                 pano.printPanoramaScript(script, optvars, pano.getOptions(), imgs, false, "");
00663             }
00664             cerr << "An error occured during optimization." << std::endl;
00665             cerr << "Try adding \"-p debug.pto\" and checking output." << std::endl;
00666             cerr << "Exiting..." << std::endl;
00667             return 1;
00668         }
00669 
00670         if (param.hdrFile.size()) {
00671             // TODO: photometric alignment (HDR, fixed white balance)
00672             //utils::StreamProgressReporter progress(2.0);
00673             //loadImgsAndExtractPoints(pano, nPoints, pyrLevel, randomPoints, progress, points);
00674             //smartOptimizePhotometric
00675 
00676             // switch to HDR output mode
00677             PanoramaOptions opts = pano.getOptions();
00678             opts.outputFormat = PanoramaOptions::HDR;
00679             opts.outputPixelType = "FLOAT";
00680             opts.outputMode = PanoramaOptions::OUTPUT_HDR;
00681             opts.outfile = param.hdrFile;
00682             pano.setOptions(opts);
00683 
00684             // remap all images
00685             StreamMultiProgressDisplay progress(cout);
00686             stitchPanorama(pano, pano.getOptions(),
00687                            progress, opts.outfile, imgs);
00688         }
00689         if (param.alignedPrefix.size()) {
00690             // disable all exposure compensation stuff.
00691             PanoramaOptions opts = pano.getOptions();
00692             opts.outputExposureValue = 0;
00693             opts.outputMode = PanoramaOptions::OUTPUT_LDR;
00694             opts.outputFormat = PanoramaOptions::TIFF_m;
00695             opts.outputPixelType = "";
00696             opts.outfile = param.alignedPrefix;
00697             pano.setOptions(opts);
00698             for (unsigned i=0; i < pano.getNrOfImages(); i++) {
00699                 SrcPanoImage img = pano.getSrcImage(i);
00700                 img.setExposureValue(0);
00701                 pano.setSrcImage(i, img);
00702             }
00703             // remap all images
00704             StreamMultiProgressDisplay progress(cout);
00705             stitchPanorama(pano, pano.getOptions(),
00706                            progress, opts.outfile, imgs);
00707 
00708         }
00709 
00710         // At this point we have panorama options set according to the output
00711         if (param.ptoFile.size() > 0) {
00712             std::ofstream script(param.ptoFile.c_str());
00713             pano.printPanoramaScript(script, optvars, pano.getOptions(), imgs, false, "");
00714         }
00715 
00716 
00717     } catch (std::exception & e) {
00718         cerr << "ERROR: caught exception: " << e.what() << std::endl;
00719         return 1;
00720     }
00721     return 0;
00722 }
00723 
00724 int main(int argc, char *argv[])
00725 {
00726     // parse arguments
00727     const char * optstring = "a:ef:g:hlmdiSAPCp:vo:s:t:c:xyz";
00728     int c;
00729 
00730     opterr = 0;
00731 
00732     g_verbose = 0;
00733 
00734     Parameters param;
00735 //    // use to override exposure value on the command line?
00736 //    std::map<std::string, double> exposureValueMap;
00737     while ((c = getopt (argc, argv, optstring)) != -1)
00738         switch (c) {
00739         case 'a':
00740             param.alignedPrefix = optarg;
00741             break;
00742         case 'c':
00743             param.nPoints = atoi(optarg);
00744             if (param.nPoints<1) {
00745                 cerr << "Invalid parameter: Number of points/grid (-c) must be at least 1" << std::endl;
00746                 return 1;
00747             }
00748             break;
00749         case 'e':
00750             param.fisheye = true;
00751             break;
00752         case 'f':
00753             param.hfov = atof(optarg);
00754             if (param.hfov<=0) {
00755                 cerr << "Invalid parameter: HFOV (-f) must be greater than 0" << std::endl;
00756                 return 1;
00757             }
00758             break;
00759         case 'g':
00760             param.grid = atoi(optarg);
00761             if (param.grid <1 || param.grid>50) {
00762                 cerr << "Invalid parameter: number of grid cells (-g) should be between 1 and 50" << std::endl;
00763                 return 1;
00764             }
00765             break;
00766         case 'l':
00767             param.linear = true;
00768             break;
00769         case 'm':
00770             param.optHFOV = true;
00771             break;
00772         case 'd':
00773             param.optDistortion = true;
00774             break;
00775         case 'i':
00776             param.optCenter = true;
00777             break;
00778         case 'x':
00779             param.optX = true;
00780             break;
00781         case 'y':
00782             param.optY = true;
00783             break;
00784         case 'z':
00785             param.optZ = true;
00786             break;
00787         case 'S':
00788             param.stereo = true;
00789             break;
00790         case 'A':
00791             param.stereo = true;
00792             param.stereo_window = true;
00793             break;
00794         case 'P':
00795             param.stereo = true;
00796             param.stereo_window = true;
00797             param.pop_out = true;
00798             break;
00799         case 'C':
00800             param.crop = true;
00801             break;
00802         case 't':
00803             param.cpErrorThreshold = atof(optarg);
00804             if (param.cpErrorThreshold <= 0) {
00805                 cerr << "Invalid parameter: control point error threshold (-t) must be greater than 0" << std::endl;
00806                 return 1;
00807             }
00808             break;
00809         case 'p':
00810             param.ptoFile = optarg;
00811             break;
00812         case 'o':
00813             param.hdrFile = optarg;
00814             break;
00815         case 'v':
00816             g_verbose++;
00817             break;
00818         case 'h':
00819             usage(argv[0]);
00820             return 0;
00821         case 's':
00822             param.pyrLevel = atoi(optarg);
00823             if (param.pyrLevel<0 || param.pyrLevel >8) {
00824                 cerr << "Invalid parameter: scaling (-s) should be between 0 and 8" << std::endl;
00825                 return 1;
00826             }
00827             break;
00828         default:
00829             cerr << "Invalid parameter: " << optarg << std::endl;
00830             usage(argv[0]);
00831             return 1;
00832         }
00833 
00834     unsigned nFiles = argc - optind;
00835     if (nFiles < 2) {
00836         std::cerr << std::endl << "Error: at least two files need to be specified" << std::endl <<std::endl;
00837         usage(argv[0]);
00838         return 1;
00839     }
00840 
00841     if (param.hdrFile.size() == 0 && param.ptoFile.size() == 0 && param.alignedPrefix.size() == 0) {
00842         std::cerr << std::endl
00843                   << "ERROR: Please specify at least one of the -p, -o or -a options." << std::endl
00844                   << std::endl;
00845         usage(argv[0]);
00846         return 1;
00847     }
00848 
00849     // extract file names
00850     std::vector<std::string> files;
00851     for (size_t i=0; i < nFiles; i++)
00852         files.push_back(argv[optind+i]);
00853 
00854     // TODO: sort images in pano by exposure
00855 
00856     std::string pixelType;
00857 
00858     try {
00859         vigra::ImageImportInfo firstImgInfo(files[0].c_str());
00860         pixelType = firstImgInfo.getPixelType();
00861     } catch (std::exception & e) {
00862         cerr << "ERROR: caught exception: " << e.what() << std::endl;
00863         return 1;
00864     }
00865 
00866     if (pixelType == "UINT8") {
00867         return main2<RGBValue<UInt8> >(files, param);
00868     } else if (pixelType == "INT16") {
00869         return main2<RGBValue<Int16> >(files, param);
00870     } else if (pixelType == "UINT16") {
00871         return main2<RGBValue<UInt16> >(files, param);
00872     } else if (pixelType == "FLOAT") {
00873         return main2<RGBValue<float> >(files, param);
00874     } else {
00875         cerr << " ERROR: unsupported pixel type: " << pixelType << std::endl;
00876         return 1;
00877     }
00878 
00879     return 0;
00880 }

Generated on Wed Jul 30 01:25:46 2014 for Hugintrunk by  doxygen 1.3.9.1