00001
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
00134 if (resp > minResponse) {
00135
00136 points.insert(make_pair(resp,Diff2D(x,y)));
00137
00138
00139
00140
00141
00142 if (points.size() > 5*nPoints) {
00143
00144 leftCorners(points.begin()->second.x,points.begin()->second.y)=0;
00145 points.erase(points.begin());
00146
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
00165 typedef std::vector<std::multimap<double, vigra::Diff2D> > MapVector;
00166
00167 if (stereo)
00168 {
00169
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
00183
00184
00185
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
00213 for (multimap<double, vigra::Diff2D>::reverse_iterator it = (*mit).rbegin();
00214 it != (*mit).rend();
00215 ++it)
00216 {
00217 if (nGood >= nPoints) {
00218
00219 break;
00220 }
00221
00222 long templWidth = 20;
00223 long sWidth = 100;
00224 long sWidth2 = scaleFactor;
00225 double corrThresh = 0.9;
00226
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
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);
00294 std::vector<int> max_i_b(pano.getNrOfImages() - 1, -1);
00295 std::vector<double> max_dif(pano.getNrOfImages() - 1, -1000000000);
00296 std::vector<double> max_dif_b(pano.getNrOfImages() - 1, -1000000000);
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;
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)
00309 max_i[cps[i].image1Nr] = i;
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
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))
00350 cps[max_i_b[i]].mode = ControlPoint::X_Y;
00351 else if (max_i[i] >= 0)
00352 cps[max_i[i]].mode = ControlPoint::X_Y;
00353 else {
00354
00355 }
00356
00357 }
00358
00359 CPVector newCPs;
00360 for (int i=0; i < (int)cps.size(); i++) {
00361 if (cps[i].mode != ControlPoint::X) {
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
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;
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;
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
00440 vigra::ImageImportInfo firstImgInfo(files[0].c_str());
00441
00442
00443 ImageType * leftImgOrig = new ImageType(firstImgInfo.size());
00444
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
00463
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
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
00482 if (param.hfov > 0) {
00483 srcImg.setHFOV(param.hfov);
00484 } else if (cropFactor == 0) {
00485
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
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
00516 opts.outputFormat = PanoramaOptions::TIFF_m;
00517 opts.tiff_saveROI = false;
00518
00519 opts.huberSigma = 2;
00520 pano.setOptions(opts);
00521
00522
00523
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
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
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
00546 srcImg.setHFOV(50);
00547 }
00548
00549 int imgNr = pano.addImage(srcImg);
00550 variable_groups.update();
00551
00552 variable_groups.getLenses().switchParts(imgNr, 0);
00553
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
00565 pano.linkImageVariableStack(imgNr, 0);
00566
00567
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
00583
00584
00585 createCtrlPoints(pano, i-1, *leftImg, *leftImgOrig, i, *rightImg, *rightImgOrig, param.pyrLevel, 2, param.nPoints, param.grid, param.stereo);
00586
00587
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
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
00629 pano.setOptimizeVector(optvars);
00630 bool optimizeError = false;
00631 optimizeError = (PTools::optimize(pano) > 0);
00632
00633
00634
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) {
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
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
00672
00673
00674
00675
00676
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
00685 StreamMultiProgressDisplay progress(cout);
00686 stitchPanorama(pano, pano.getOptions(),
00687 progress, opts.outfile, imgs);
00688 }
00689 if (param.alignedPrefix.size()) {
00690
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
00704 StreamMultiProgressDisplay progress(cout);
00705 stitchPanorama(pano, pano.getOptions(),
00706 progress, opts.outfile, imgs);
00707
00708 }
00709
00710
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
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
00736
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
00850 std::vector<std::string> files;
00851 for (size_t i=0; i < nFiles; i++)
00852 files.push_back(argv[optind+i]);
00853
00854
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 }