00001
00026 #ifndef _NONA_STITCHER_H
00027 #define _NONA_STITCHER_H
00028
00029
00030 #include <vigra/windows.h>
00031
00032 #include <sstream>
00033 #include <iomanip>
00034 #include <vector>
00035 #include <utility>
00036 #include <cctype>
00037 #include <algorithm>
00038
00039 #include <vigra/stdimage.hxx>
00040 #include <vigra/rgbvalue.hxx>
00041 #include <vigra/tiff.hxx>
00042
00043 #include <vigra/impex.hxx>
00044 #include <vigra_ext/impexalpha.hxx>
00045
00046 #include <vigra_ext/blend.h>
00047 #include <vigra_ext/NearestFeatureTransform.h>
00048 #include <vigra_ext/tiffUtils.h>
00049 #include <vigra_ext/ImageTransforms.h>
00050
00051 #include <panodata/PanoramaData.h>
00052 #include <algorithms/nona/ComputeImageROI.h>
00053 #include <nona/RemappedPanoImage.h>
00054 #include <nona/ImageRemapper.h>
00055
00056
00057 #define STITCHER_CALC_DIST_IMG 0
00058
00059
00060 #undef DIFFERENCE
00061 #undef min
00062 #undef max
00063 #undef MIN
00064 #undef MAX
00065
00066 namespace HuginBase {
00067 namespace Nona {
00068
00069
00074 void estimateBlendingOrder(const PanoramaData & pano, UIntSet images,
00075 std::vector<unsigned int> & blendOrder);
00076
00077 #if 0
00078
00083 template <typename ImageType, typename AlphaType>
00084 bool remapToTiff(const PanoramaData & pano, const PanoramaOptions &opts,
00085 unsigned imgNr, TIFF * tiff
00086 AppBase::MultiProgressDisplay & progress)
00087 {
00088 bool saveRoi = opts.tiff_saveROI;
00089
00090 const PanoImage & img = pano.getImage(imgNr);
00091 vigra::Diff2D srcSize;
00092 srcSize.x = img.getWidth();
00093 srcSize.y = img.getHeight();
00094
00095
00096
00097
00098 PTools::Transform transf;
00099 PTools::Transform invTransf;
00100
00101 transf.createTransform(pano, imgNr, opts, srcSize);
00102 invTransf.createInvTransform(pano, imgNr, opts, srcSize);
00103
00104
00105
00106 ImageOptions imgOpts = img.getOptions();
00107 vigra::Rect2D imageRect;
00108 vigra::BImage miniAlpha;
00109 double alphaScale;
00110 bool circCrop = pano.getLens(pano.getImage(imgNr).getLensNr()).getProjection() == Lens::CIRCULAR_FISHEYE;
00111 estimateImageRect(Size2D(opts.width, opts.getHeight()), srcSize,
00112 imgOpts.docrop, imgOpts.cropRect, circCrop,
00113 transf,
00114 imageRect, miniAlpha, alphaScale);
00115
00117 ImageType img(;
00118 AlphaType alpha;
00119
00120
00121 if (saveRoi) {
00122 vigra_ext::createTiffDirectory(tiff,
00123 img.getFilename(),
00124 m_basename,
00125 opts.tiffCompression,
00126 imgNr+1, nImg,
00127 imageRect.upperLeft());
00128 } else {
00129 vigra_ext::createTiffDirectory(tiff,
00130 img.getFilename(),
00131 m_basename,
00132 opts.tiffCompression,
00133 imgNr+1, nImg,
00134 vigra::Diff2D(0,0));
00135 }
00136 #endif
00137
00139 template <typename ImageType, typename AlphaType>
00140 class Stitcher
00141 {
00142 public:
00144 Stitcher(const PanoramaData & pano, AppBase::MultiProgressDisplay & prog)
00145 : m_pano(pano), m_progress(prog)
00146 {
00147 }
00148
00149 virtual ~Stitcher() {};
00150
00155 virtual void stitch(const PanoramaOptions & opts,
00156 UIntSet & images, const std::string & file,
00157 SingleImageRemapper<ImageType, AlphaType> & remapper)
00158 {
00159 m_images=images;
00160 calcOutputROIS(opts, images);
00161 };
00162
00163
00164 virtual UIntSet getUsedImages()
00165 {
00166 UIntSet ret;
00167 assert(m_rois.size() == m_images.size());
00168 std::vector<vigra::Rect2D>::iterator itr = m_rois.begin();
00169 for(UIntSet::iterator it = m_images.begin(); it != m_images.end(); ++it) {
00170 if (! itr->isEmpty()) {
00171 ret.insert(*it);
00172 }
00173 }
00174 return ret;
00175 }
00176
00177
00178
00179
00180 protected:
00181
00182 virtual void calcOutputROIS(const PanoramaOptions & opts, const UIntSet & images)
00183 {
00184 m_rois = HuginBase::ComputeImageROI::computeROIS(m_pano, opts, images);
00185 }
00186
00187 const PanoramaData & m_pano;
00188 AppBase::MultiProgressDisplay & m_progress;
00189 UIntSet m_images;
00190 std::vector<vigra::Rect2D> m_rois;
00191 };
00192
00194 template <typename ImageType, typename AlphaType>
00195 class MultiImageRemapper : public Stitcher<ImageType, AlphaType>
00196 {
00197 public:
00198
00199 typedef Stitcher<ImageType, AlphaType> Base;
00200
00201 MultiImageRemapper(const PanoramaData & pano,
00202 AppBase::MultiProgressDisplay & progress)
00203 : Stitcher<ImageType,AlphaType>(pano, progress)
00204 {
00205 }
00206
00207 virtual ~MultiImageRemapper()
00208 {
00209 }
00210
00211 virtual void stitch(const PanoramaOptions & opts, UIntSet & images,
00212 const std::string & basename,
00213 SingleImageRemapper<ImageType, AlphaType> & remapper)
00214 {
00215 Base::stitch(opts, images, basename, remapper);
00216 DEBUG_ASSERT(opts.outputFormat == PanoramaOptions::TIFF_multilayer
00217 || opts.outputFormat == PanoramaOptions::TIFF_m
00218 || opts.outputFormat == PanoramaOptions::HDR_m
00219 || opts.outputFormat == PanoramaOptions::EXR_m);
00220
00221 m_basename = basename;
00222
00223
00224 prepareOutputFile(opts);
00225
00226 unsigned int nImg = images.size();
00227 Base::m_progress.pushTask(AppBase::ProgressTask("Remapping", "", 1.0/(nImg)));
00228
00229 int i=0;
00230 for (UIntSet::const_iterator it = images.begin();
00231 it != images.end(); ++it)
00232 {
00233
00234 RemappedPanoImage<ImageType, AlphaType> *
00235 remapped = remapper.getRemapped(Base::m_pano, opts, *it,
00236 Base::m_rois[i], Base::m_progress);
00237 try {
00238 saveRemapped(*remapped, *it, Base::m_pano.getNrOfImages(), opts);
00239 } catch (vigra::PreconditionViolation & e) {
00240
00241
00242 std::cerr << e.what();
00243 }
00244
00245 remapper.release(remapped);
00246 i++;
00247 }
00248 Base::m_progress.popTask();
00249 finalizeOutputFile(opts);
00250 }
00251
00253 virtual void prepareOutputFile(const PanoramaOptions & opts)
00254 {
00255 }
00256
00258 virtual void saveRemapped(RemappedPanoImage<ImageType, AlphaType> & remapped,
00259 unsigned int imgNr, unsigned int nImg,
00260 const PanoramaOptions & opts)
00261 {
00262 ImageType * final_img = 0;
00263 AlphaType * alpha_img = 0;
00264 ImageType complete;
00265 vigra::BImage alpha;
00266
00267 if ( remapped.boundingBox().isEmpty() )
00268
00269
00270 return;
00271
00272 if (opts.outputMode == PanoramaOptions::OUTPUT_HDR) {
00273
00274 std::ostringstream greyname;
00275 greyname << m_basename << std::setfill('0') << std::setw(4) << imgNr << "_gray.pgm";
00276 vigra::ImageExportInfo exinfo1(greyname.str().c_str());
00277 if (! opts.tiff_saveROI) {
00278 alpha.resize(opts.getROI().size());
00279 vigra::Rect2D newOutRect = remapped.boundingBox() & opts.getROI();
00280 vigra::Rect2D newOutArea(newOutRect);
00281 newOutRect.moveBy(-opts.getROI().upperLeft());
00282 vigra::copyImage(vigra_ext::applyRect(newOutArea,
00283 vigra_ext::srcMaskRange(remapped)),
00284 vigra_ext::applyRect(newOutRect,
00285 destImage(alpha)));
00286 vigra::exportImage(srcImageRange(alpha), exinfo1);
00287 } else {
00288 exinfo1.setPosition(remapped.boundingBox().upperLeft());
00289 exinfo1.setCanvasSize(vigra::Size2D(opts.getWidth(), opts.getHeight()));
00290 vigra::Rect2D rect=remapped.boundingBox();
00291 if(rect.right() > opts.getROI().right()) {
00292 rect&=opts.getROI();
00293 rect.moveBy(-rect.upperLeft());
00294 vigra::exportImage(srcImageRange(remapped.m_mask, rect), exinfo1);
00295 } else {
00296 vigra::exportImage(srcImageRange(remapped.m_mask), exinfo1);
00297 };
00298 }
00299
00300
00301 Base::m_progress.setMessage("Calculating mask");
00302 remapped.calcAlpha();
00303 }
00304
00305 vigra::Rect2D subImage;
00306 if (! opts.tiff_saveROI) {
00307
00308
00309 complete.resize(opts.getROI().size());
00310 alpha.resize(opts.getROI().size());
00311 vigra::Rect2D newOutRect = remapped.boundingBox() & opts.getROI();
00312 vigra::Rect2D newOutArea(newOutRect);
00313 newOutRect.moveBy(-opts.getROI().upperLeft());
00314 vigra::copyImage(vigra_ext::applyRect(newOutArea,
00315 vigra_ext::srcImageRange(remapped)),
00316 vigra_ext::applyRect(newOutRect,
00317 destImage(complete)));
00318
00319 vigra::copyImage(vigra_ext::applyRect(newOutArea,
00320 vigra_ext::srcMaskRange(remapped)),
00321 vigra_ext::applyRect(newOutRect,
00322 destImage(alpha)));
00323 final_img = & complete;
00324 alpha_img = & alpha;
00325 } else {
00326 final_img = &remapped.m_image;
00327 alpha_img = &remapped.m_mask;
00328 if(remapped.boundingBox().right() > opts.getROI().right()) {
00329 subImage=remapped.boundingBox() & opts.getROI();
00330 subImage.moveBy(-subImage.upperLeft());
00331 };
00332 }
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343 std::string ext = opts.getOutputExtension();
00344 std::ostringstream filename;
00345 filename << m_basename << std::setfill('0') << std::setw(4) << imgNr << "." + ext;
00346
00347
00348 Base::m_progress.setMessage(std::string("saving ") +
00349 hugin_utils::stripPath(filename.str()));
00350
00351 vigra::ImageExportInfo exinfo(filename.str().c_str());
00352 exinfo.setXResolution(150);
00353 exinfo.setYResolution(150);
00354 exinfo.setICCProfile(remapped.m_ICCProfile);
00355 if (opts.tiff_saveROI) {
00356 exinfo.setPosition(remapped.boundingBox().upperLeft());
00357 exinfo.setCanvasSize(vigra::Size2D(opts.getWidth(), opts.getHeight()));
00358 } else {
00359 exinfo.setPosition(opts.getROI().upperLeft());
00360 exinfo.setCanvasSize(vigra::Size2D(opts.getWidth(), opts.getHeight()));
00361 }
00362 if (opts.outputPixelType.size() > 0) {
00363 exinfo.setPixelType(opts.outputPixelType.c_str());
00364 }
00365 if (ext == "tif") {
00366 exinfo.setCompression(opts.tiffCompression.c_str());
00367 }
00368
00369 if(subImage.area()>0) {
00370 vigra::exportImageAlpha(srcImageRange(*final_img, subImage), srcImage(*alpha_img), exinfo);
00371 } else {
00372 vigra::exportImageAlpha(srcImageRange(*final_img), srcImage(*alpha_img), exinfo);
00373 };
00374
00375 if (opts.saveCoordImgs) {
00376 vigra::UInt16Image xImg;
00377 vigra::UInt16Image yImg;
00378
00379 Base::m_progress.setMessage("creating coordinate images");
00380
00381 remapped.calcSrcCoordImgs(xImg, yImg);
00382 vigra::UInt16Image dist;
00383 if (! opts.tiff_saveROI) {
00384 dist.resize(opts.getWidth(), opts.getHeight());
00385 vigra::copyImage(srcImageRange(xImg),
00386 vigra_ext::applyRect(remapped.boundingBox(),
00387 destImage(dist)));
00388 std::ostringstream filename2;
00389 filename2 << m_basename << std::setfill('0') << std::setw(4) << imgNr << "_x.tif";
00390
00391 vigra::ImageExportInfo exinfo(filename2.str().c_str());
00392 exinfo.setXResolution(150);
00393 exinfo.setYResolution(150);
00394 exinfo.setCompression(opts.tiffCompression.c_str());
00395 vigra::exportImage(srcImageRange(dist), exinfo);
00396
00397
00398 vigra::copyImage(srcImageRange(yImg),
00399 vigra_ext::applyRect(remapped.boundingBox(),
00400 destImage(dist)));
00401 std::ostringstream filename3;
00402 filename3 << m_basename << std::setfill('0') << std::setw(4) << imgNr << "_y.tif";
00403
00404 vigra::ImageExportInfo exinfo2(filename3.str().c_str());
00405 exinfo2.setXResolution(150);
00406 exinfo2.setYResolution(150);
00407 exinfo2.setCompression(opts.tiffCompression.c_str());
00408 vigra::exportImage(srcImageRange(dist), exinfo2);
00409 } else {
00410 std::ostringstream filename2;
00411 filename2 << m_basename << std::setfill('0') << std::setw(4) << imgNr << "_x.tif";
00412 vigra::ImageExportInfo exinfo(filename2.str().c_str());
00413 exinfo.setXResolution(150);
00414 exinfo.setYResolution(150);
00415 exinfo.setCompression(opts.tiffCompression.c_str());
00416 vigra::exportImage(srcImageRange(xImg), exinfo);
00417 std::ostringstream filename3;
00418 filename3 << m_basename << std::setfill('0') << std::setw(4) << imgNr << "_y.tif";
00419 vigra::ImageExportInfo exinfo2(filename3.str().c_str());
00420 exinfo2.setXResolution(150);
00421 exinfo2.setYResolution(150);
00422 exinfo2.setCompression(opts.tiffCompression.c_str());
00423 vigra::exportImage(srcImageRange(yImg), exinfo2);
00424 }
00425 }
00426 }
00427
00428 virtual void finalizeOutputFile(const PanoramaOptions & opts)
00429 {
00430 }
00431
00432 protected:
00433 std::string m_basename;
00434 };
00435
00436
00437
00439 template <typename ImageType, typename AlphaImageType>
00440 class TiffMultiLayerRemapper : public MultiImageRemapper<ImageType, AlphaImageType>
00441 {
00442 public:
00443
00444 typedef MultiImageRemapper<ImageType, AlphaImageType> Base;
00445 TiffMultiLayerRemapper(const PanoramaData & pano,
00446 AppBase::MultiProgressDisplay & progress)
00447 : MultiImageRemapper<ImageType, AlphaImageType> (pano, progress)
00448 {
00449 }
00450
00451 virtual ~TiffMultiLayerRemapper()
00452 {
00453 }
00454
00455 virtual void prepareOutputFile(const PanoramaOptions & opts)
00456 {
00457 std::string filename = Base::m_basename + ".tif";
00458 DEBUG_DEBUG("Layering image into a multi image tif file " << filename);
00459 m_tiff = TIFFOpen(filename.c_str(), "w");
00460 DEBUG_ASSERT(m_tiff && "could not open tiff output file");
00461 }
00462
00464 virtual void saveRemapped(RemappedPanoImage<ImageType, AlphaImageType> & remapped,
00465 unsigned int imgNr, unsigned int nImg,
00466 const PanoramaOptions & opts)
00467 {
00468 if (remapped.boundingBox().isEmpty())
00469 return;
00470
00471 DEBUG_DEBUG("Saving TIFF ROI");
00472 vigra_ext::createTiffDirectory(m_tiff,
00473 Base::m_pano.getImage(imgNr).getFilename(),
00474 Base::m_basename,
00475 opts.tiffCompression,
00476 imgNr+1, nImg, remapped.boundingBox().upperLeft(),
00477 opts.getROI().size(),
00478 remapped.m_ICCProfile);
00479 vigra_ext::createAlphaTiffImage(vigra::srcImageRange(remapped.m_image),
00480 vigra::maskImage(remapped.m_mask),
00481 m_tiff);
00482 TIFFFlush(m_tiff);
00483 }
00484
00486 virtual void finalizeOutputFile(const PanoramaOptions & opts)
00487 {
00488 TIFFClose(m_tiff);
00489 }
00490
00491
00492 protected:
00493 vigra::TiffImage * m_tiff;
00494 };
00495
00496
00497 typedef std::vector<std::pair<float, unsigned int> > AlphaVector;
00498
00502 struct CalcMaskUnion
00503 {
00504 CalcMaskUnion()
00505 : count(0)
00506 { }
00507
00508 template<typename PIXEL>
00509 PIXEL operator()(PIXEL const & img1, PIXEL const & img2)
00510 {
00511 if (img1 > vigra::NumericTraits<PIXEL>::zero() && img2 > vigra::NumericTraits<PIXEL>::zero()) {
00512 count++;
00513 return img1;
00514 } else {
00515 return vigra::NumericTraits<PIXEL>::zero();
00516 }
00517 }
00518
00519 int count;
00520 };
00521
00522
00523 template <typename ImageType, typename AlphaType>
00524 class WeightedStitcher : public Stitcher<ImageType, AlphaType>
00525 {
00526 public:
00527
00528 typedef Stitcher<ImageType, AlphaType> Base;
00529 WeightedStitcher(const PanoramaData & pano,
00530 AppBase::MultiProgressDisplay & progress)
00531 : Stitcher<ImageType, AlphaType>(pano, progress)
00532 {
00533 }
00534
00535 virtual ~WeightedStitcher() {};
00536
00537 template<class ImgIter, class ImgAccessor,
00538 class AlphaIter, class AlphaAccessor>
00539 void stitch(const PanoramaOptions & opts, UIntSet & imgSet,
00540 vigra::triple<ImgIter, ImgIter, ImgAccessor> pano,
00541 std::pair<AlphaIter, AlphaAccessor> alpha,
00542 SingleImageRemapper<ImageType, AlphaType> & remapper)
00543 {
00544 std::vector<unsigned int> images;
00545
00546 estimateBlendingOrder(Base::m_pano, imgSet, images);
00547
00548 unsigned int nImg = images.size();
00549
00550 Base::m_progress.pushTask(AppBase::ProgressTask("Stitching", "", 1.0/(nImg)));
00551
00552 vigra::Rect2D panoROI;
00553
00554 int i=0;
00555
00556 for (UIntVector::const_iterator it = images.begin();
00557 it != images.end(); ++it)
00558 {
00559
00560 DEBUG_DEBUG("remapping image: " << *it);
00561 RemappedPanoImage<ImageType, AlphaType> *
00562 remapped = remapper.getRemapped(Base::m_pano, opts, *it,
00563 Base::m_rois[i], Base::m_progress);
00564 if(iccProfile.size()==0)
00565 {
00566 iccProfile=remapped->m_ICCProfile;
00567 };
00568 Base::m_progress.setMessage("blending");
00569
00570 try {
00571 vigra_ext::blend(*remapped, pano, alpha, panoROI,
00572 Base::m_progress);
00573
00574 panoROI = panoROI | remapped->boundingBox();
00575 } catch (vigra::PreconditionViolation & e) {
00576 DEBUG_ERROR("exception during stitching" << e.what());
00577
00578
00579 }
00580
00581 remapper.release(remapped);
00582 i++;
00583 }
00584 }
00585
00586 void stitch(const PanoramaOptions & opts, UIntSet & imgSet,
00587 const std::string & filename,
00588 SingleImageRemapper<ImageType, AlphaType> & remapper)
00589 {
00590 Base::stitch(opts, imgSet, filename, remapper);
00591
00592 std::string basename = filename;
00593
00594
00595 ImageType pano(opts.getWidth(), opts.getHeight());
00596 AlphaType panoMask(opts.getWidth(), opts.getHeight());
00597
00598 stitch(opts, imgSet, vigra::destImageRange(pano), vigra::destImage(panoMask), remapper);
00599
00600 std::string ext = opts.getOutputExtension();
00601 std::string cext = hugin_utils::getExtension(basename);
00602 std::transform(cext.begin(),cext.end(), cext.begin(), (int(*)(int))std::tolower);
00603
00604
00605 if (cext == ext) {
00606 basename = hugin_utils::stripExtension(basename);
00607 }
00608 std::string outputfile = basename + "." + ext;
00609
00610
00611 Base::m_progress.setMessage("saving result: " + hugin_utils::stripPath(outputfile));
00612 DEBUG_DEBUG("Saving panorama: " << outputfile);
00613 vigra::ImageExportInfo exinfo(outputfile.c_str());
00614 exinfo.setXResolution(150);
00615 exinfo.setYResolution(150);
00616 exinfo.setICCProfile(iccProfile);
00617 if (opts.outputPixelType.size() > 0) {
00618 exinfo.setPixelType(opts.outputPixelType.c_str());
00619 }
00620
00621
00622 if (opts.outputFormat == PanoramaOptions::JPEG) {
00623 char jpgCompr[4];
00624 snprintf(jpgCompr,4,"%d", opts.quality);
00625 exinfo.setCompression(jpgCompr);
00626 vigra::exportImage(srcImageRange(pano), exinfo);
00627 } else if (opts.outputFormat == PanoramaOptions::TIFF) {
00628 exinfo.setCompression(opts.tiffCompression.c_str());
00629 vigra::exportImageAlpha(srcImageRange(pano),
00630 srcImage(panoMask), exinfo);
00631 } else if (opts.outputFormat == PanoramaOptions::HDR) {
00632 exinfo.setPixelType("FLOAT");
00633 vigra::exportImage(srcImageRange(pano), exinfo);
00634 } else {
00635 vigra::exportImageAlpha(srcImageRange(pano),
00636 srcImage(panoMask), exinfo);
00637 }
00638
00639
00640
00641
00642
00643 Base::m_progress.popTask();
00644
00645 }
00646
00647 protected:
00648 vigra::ImageImportInfo::ICCProfile iccProfile;
00649 };
00650
00651
00653 template<class VALUETYPE>
00654 struct ReduceToDifferenceFunctor
00655 {
00656 typedef VALUETYPE argument_type;
00657 typedef VALUETYPE result_type;
00658 typedef typename vigra::NumericTraits<argument_type> Traits;
00659 typedef typename Traits::RealPromote float_type;
00660
00661
00662 ReduceToDifferenceFunctor()
00663 {
00664 reset();
00665 }
00666
00667 void reset()
00668 {
00669 sum = vigra::NumericTraits<float_type>::zero();
00670 values.clear();
00671 }
00672
00673 template<class T, class M>
00674 void operator() (T const &v, M const &a)
00675 {
00676 if (a) {
00677 values.push_back(v);
00678 sum = sum + v;
00679 }
00680 }
00681
00683 float_type operator()() const
00684 {
00685 typedef typename std::vector<float_type>::const_iterator Iter;
00686 if (values.size() > 1) {
00687 float_type mean = sum / values.size();
00688 float_type error = vigra::NumericTraits<float_type>::zero();
00689 for (Iter it= values.begin(); it != values.end(); ++it) {
00690 error += vigra::abs(*it-mean);
00691 }
00692 return error;
00693 } else {
00694 return sum;
00695 }
00696 }
00697 std::vector<float_type> values;
00698 float_type sum;
00699 };
00700
00705 template <typename ImageType, typename AlphaType>
00706 class ReduceStitcher : public Stitcher<ImageType, AlphaType>
00707 {
00708 typedef Stitcher<ImageType, AlphaType> Base;
00709 public:
00710 ReduceStitcher(const PanoramaData & pano,
00711 AppBase::MultiProgressDisplay & progress)
00712 : Stitcher<ImageType, AlphaType>(pano, progress)
00713 {
00714 }
00715
00716 virtual ~ReduceStitcher()
00717 {
00718 }
00719
00720 template <class FUNCTOR>
00721 void stitch(const PanoramaOptions & opts, UIntSet & imgSet,
00722 const std::string & filename,
00723 SingleImageRemapper<ImageType, AlphaType> & remapper,
00724 FUNCTOR & reduce)
00725 {
00726 Base::stitch(opts, imgSet, filename, remapper);
00727
00728 std::string basename = filename;
00729
00730
00731 ImageType pano(opts.getWidth(), opts.getHeight());
00732 AlphaType panoMask(opts.getWidth(), opts.getHeight());
00733
00734 stitch(opts, imgSet, vigra::destImageRange(pano), vigra::destImage(panoMask),
00735 remapper, reduce);
00736
00737 std::string ext = opts.getOutputExtension();
00738 std::string cext = hugin_utils::getExtension(basename);
00739 std::transform(cext.begin(),cext.end(), cext.begin(), (int(*)(int))std::tolower);
00740
00741
00742 if (cext == ext) {
00743 basename = hugin_utils::stripExtension(basename);
00744 }
00745 std::string outputfile = basename + "." + ext;
00746
00747
00748 DEBUG_DEBUG("Saving panorama: " << outputfile);
00749 vigra::ImageExportInfo exinfo(outputfile.c_str());
00750 if (opts.outputPixelType.size() > 0) {
00751 exinfo.setPixelType(opts.outputPixelType.c_str());
00752 }
00753 exinfo.setXResolution(150);
00754 exinfo.setYResolution(150);
00755 exinfo.setICCProfile(iccProfile);
00756
00757 if (opts.outputFormat == PanoramaOptions::JPEG) {
00758 char jpgCompr[4];
00759 snprintf(jpgCompr,4,"%d", opts.quality);
00760 exinfo.setCompression(jpgCompr);
00761 vigra::exportImage(srcImageRange(pano), exinfo);
00762 } else if (opts.outputFormat == PanoramaOptions::TIFF) {
00763 exinfo.setCompression(opts.tiffCompression.c_str());
00764 vigra::exportImageAlpha(srcImageRange(pano),
00765 srcImage(panoMask), exinfo);
00766 } else if (opts.outputFormat == PanoramaOptions::HDR) {
00767 vigra::exportImage(srcImageRange(pano), exinfo);
00768 } else {
00769 vigra::exportImageAlpha(srcImageRange(pano),
00770 srcImage(panoMask), exinfo);
00771 }
00772
00773
00774
00775
00776
00777 }
00778
00779
00780 template<class ImgIter, class ImgAccessor,
00781 class AlphaIter, class AlphaAccessor,
00782 class FUNCTOR>
00783 void stitch(const PanoramaOptions & opts, UIntSet & imgSet,
00784 vigra::triple<ImgIter, ImgIter, ImgAccessor> pano,
00785 std::pair<AlphaIter, AlphaAccessor> alpha,
00786 SingleImageRemapper<ImageType, AlphaType> & remapper,
00787 FUNCTOR & reduce)
00788 {
00789 typedef typename
00790 vigra::NumericTraits<typename ImageType::value_type> Traits;
00791 typedef typename
00792 Traits::RealPromote RealImgType;
00793 typedef typename ImageType::value_type ImgType;
00794 typedef typename AlphaAccessor::value_type MaskType;
00795
00796 Base::stitch(opts, imgSet, "dummy", remapper);
00797
00798
00799 typedef std::vector<RemappedPanoImage<ImageType, AlphaType> *> RemappedVector;
00800 unsigned int nImg = imgSet.size();
00801
00802 Base::m_progress.pushTask(AppBase::ProgressTask("Stitching", "", 1.0/(nImg)));
00803
00804
00805
00806 RemappedVector remapped(nImg);
00807
00808 int i=0;
00809
00810 for (UIntSet::const_iterator it = imgSet.begin();
00811 it != imgSet.end(); ++it)
00812 {
00813
00814
00815
00816 remapped[i] = remapper.getRemapped(Base::m_pano, opts, *it,
00817 Base::m_rois[i], Base::m_progress);
00818 if(iccProfile.size()==0)
00819 {
00820 iccProfile=remapped[i]->m_ICCProfile;
00821 };
00822 i++;
00823 }
00824 vigra::Diff2D size = pano.second - pano.first;
00825 ImgIter output = pano.first;
00826
00827
00828 for (int y=0; y < size.y; y++) {
00829 for (int x=0; x < size.x; x++) {
00830 reduce.reset();
00831 MaskType maskRes=0;
00832 for (unsigned int i=0; i< nImg; i++) {
00833 MaskType a = remapped[i]->getMask(x,y);
00834 if (a) {
00835 maskRes = vigra_ext::LUTTraits<MaskType>::max();
00836 reduce(remapped[i]->operator()(x,y), a);
00837 }
00838 }
00839 pano.third.set(Traits::fromRealPromote(reduce()), output, vigra::Diff2D(x,y));
00840 alpha.second.set(maskRes, alpha.first, vigra::Diff2D(x,y));
00841 }
00842 }
00843 Base::m_progress.popTask();
00844
00845 for (typename RemappedVector::iterator it=remapped.begin();
00846 it != remapped.end(); ++it)
00847 {
00848 remapper.release(*it);
00849 }
00850 }
00851
00852 protected:
00853 vigra::ImageImportInfo::ICCProfile iccProfile;
00854 };
00855
00858 template <typename ImageType, typename AlphaType>
00859 class SimpleStitcher : public Stitcher<ImageType, AlphaType>
00860 {
00861 typedef Stitcher<ImageType, AlphaType> Base;
00862 public:
00863 SimpleStitcher(const PanoramaData & pano,
00864 AppBase::MultiProgressDisplay & progress)
00865 : Stitcher<ImageType, AlphaType>(pano, progress)
00866 {
00867 }
00868
00869 virtual ~SimpleStitcher()
00870 {
00871 }
00872
00873 template<class ImgIter, class ImgAccessor,
00874 class AlphaIter, class AlphaAccessor,
00875 class BlendFunctor>
00876 void stitch(const PanoramaOptions & opts, UIntSet & imgSet,
00877 vigra::triple<ImgIter, ImgIter, ImgAccessor> pano,
00878 std::pair<AlphaIter, AlphaAccessor> alpha,
00879 SingleImageRemapper<ImageType, AlphaType> & remapper,
00880 BlendFunctor & blend)
00881 {
00882 Base::m_images=imgSet;
00883 Base::calcOutputROIS(opts, imgSet);
00884
00885 unsigned int nImg = imgSet.size();
00886
00887 Base::m_progress.pushTask(AppBase::ProgressTask("Stitching", "", 1.0/(nImg)));
00888
00889 vigra::Rect2D panoROI;
00890
00891 unsigned i=0;
00892
00893 for (UIntSet::reverse_iterator it = imgSet.rbegin();
00894 it != imgSet.rend(); ++it)
00895 {
00896
00897 RemappedPanoImage<ImageType, AlphaType> *
00898 remapped = remapper.getRemapped(Base::m_pano, opts, *it,
00899 Base::m_rois[i], Base::m_progress);
00900 if (iccProfile.size() == 0) {
00901
00902 iccProfile = remapped->m_ICCProfile;
00903 }
00904 Base::m_progress.setMessage("blending");
00905
00906 try {
00907 blend(*remapped, pano, alpha, panoROI);
00908
00909 panoROI = panoROI | remapped->boundingBox();
00910 } catch (vigra::PreconditionViolation & e) {
00911
00912
00913 std::cerr << e.what();
00914 }
00915
00916 remapper.release(remapped);
00917 i++;
00918 }
00919 Base::m_progress.popTask();
00920 }
00921
00922 template <class BlendFunctor>
00923 void stitch(const PanoramaOptions & opts, UIntSet & imgSet,
00924 const std::string & filename,
00925 SingleImageRemapper<ImageType, AlphaType> & remapper,
00926 BlendFunctor & blend)
00927 {
00928 std::string basename = filename;
00929
00930
00931 ImageType pano(opts.getWidth(), opts.getHeight());
00932 AlphaType panoMask(opts.getWidth(), opts.getHeight());
00933
00934 stitch(opts, imgSet, vigra::destImageRange(pano), vigra::destImage(panoMask), remapper, blend);
00935
00936 std::string ext = opts.getOutputExtension();
00937 std::string cext = hugin_utils::getExtension(basename);
00938 std::transform(cext.begin(),cext.end(), cext.begin(), (int(*)(int))std::tolower);
00939
00940
00941 if (cext == ext) {
00942 basename = hugin_utils::stripExtension(basename);
00943 }
00944 std::string outputfile = basename + "." + ext;
00945
00946 Base::m_progress.setMessage("saving result: " + hugin_utils::stripPath(outputfile));
00947 DEBUG_DEBUG("Saving panorama: " << outputfile);
00948 vigra::ImageExportInfo exinfo(outputfile.c_str());
00949 exinfo.setXResolution(150);
00950 exinfo.setYResolution(150);
00951 exinfo.setICCProfile(iccProfile);
00952
00953 if (opts.outputFormat == PanoramaOptions::JPEG) {
00954 char jpgCompr[4];
00955 snprintf(jpgCompr,4,"%d", opts.quality);
00956 exinfo.setCompression(jpgCompr);
00957 vigra::exportImage(srcImageRange(pano), exinfo);
00958 } else if (opts.outputFormat == PanoramaOptions::TIFF) {
00959 exinfo.setCompression("DEFLATE");
00960 vigra::exportImageAlpha(srcImageRange(pano),
00961 srcImage(panoMask), exinfo);
00962 } else {
00963 vigra::exportImageAlpha(srcImageRange(pano),
00964 srcImage(panoMask), exinfo);
00965 }
00966
00967
00968
00969
00970
00971 Base::m_progress.popTask();
00972
00973 }
00974 protected:
00975 vigra::ImageExportInfo::ICCProfile iccProfile;
00976 };
00977
00981 struct StackingBlender
00982 {
00983
00984 public:
00989 template <typename ImageType, typename AlphaType,
00990 typename PanoIter, typename PanoAccessor,
00991 typename AlphaIter, typename AlphaAccessor>
00992 void operator()(RemappedPanoImage<ImageType, AlphaType> & img,
00993 vigra::triple<PanoIter, PanoIter, PanoAccessor> pano,
00994 std::pair<AlphaIter, AlphaAccessor> alpha,
00995 const vigra::Rect2D & panoROI)
00996 {
00997 using namespace vigra_ext;
00998
00999 DEBUG_DEBUG("pano roi: " << panoROI << " img roi: " << img.boundingBox());
01000 typedef typename AlphaIter::value_type AlphaValue;
01001
01002
01003
01004 DEBUG_DEBUG("size of panorama: " << pano.second - pano.first);
01005
01006
01007 vigra::Rect2D fullPano(vigra::Size2D(pano.second-pano.first));
01008
01009 vigra::Rect2D overlap = fullPano & img.boundingBox();
01010
01011 vigra::copyImageIf(applyRect(overlap, vigra_ext::srcImageRange(img)),
01012 applyRect(overlap, vigra_ext::srcMask(img)),
01013 applyRect(overlap, std::make_pair(pano.first, pano.third)));
01014
01015 vigra::copyImageIf(applyRect(overlap, srcMaskRange(img)),
01016 applyRect(overlap, srcMask(img)),
01017 applyRect(overlap, alpha));
01018 }
01019 };
01020
01022 struct SeamBlender
01023 {
01024 public:
01025
01030 template <typename ImageType, typename AlphaType,
01031 typename PanoIter, typename PanoAccessor,
01032 typename AlphaIter, typename AlphaAccessor>
01033 void operator()(RemappedPanoImage<ImageType, AlphaType> & img,
01034 vigra::triple<PanoIter, PanoIter, PanoAccessor> pano,
01035 std::pair<AlphaIter, AlphaAccessor> alpha,
01036 const vigra::Rect2D & panoROI)
01037 {
01038 DEBUG_DEBUG("pano roi: " << panoROI << " img roi: " << img.boundingBox());
01039 AppBase::MultiProgressDisplay dummy;
01040 vigra_ext::blend(img, pano, alpha, panoROI, dummy);
01041 }
01042 };
01043
01045 struct DifferenceBlender
01046 {
01047 public:
01048
01053 template <typename ImageType, typename AlphaType,
01054 typename PanoIter, typename PanoAccessor,
01055 typename AlphaIter, typename AlphaAccessor>
01056 void operator()(RemappedPanoImage<ImageType, AlphaType> & img,
01057 vigra::triple<PanoIter, PanoIter, PanoAccessor> pano,
01058 std::pair<AlphaIter, AlphaAccessor> alpha,
01059 const vigra::Rect2D & panoROI)
01060 {
01061 DEBUG_DEBUG("pano roi: " << panoROI << " img roi: " << img.boundingBox());
01062 typedef typename AlphaIter::value_type AlphaValue;
01063
01064
01065 vigra::Rect2D fullPano(vigra::Size2D(pano.second-pano.first));
01066
01067 vigra::Rect2D overlap = fullPano & img.boundingBox();
01068
01069 vigra::combineTwoImagesIf(applyRect(overlap, vigra_ext::srcImageRange(img)),
01070 applyRect(overlap, std::make_pair(pano.first, pano.third)),
01071 applyRect(overlap, vigra_ext::srcMask(img)),
01072 applyRect(overlap, std::make_pair(pano.first, pano.third)),
01073 abs(vigra::functor::Arg1()-vigra::functor::Arg2()));
01074
01075
01076 vigra::copyImageIf(applyRect(overlap, srcMaskRange(img)),
01077 applyRect(overlap, srcMask(img)),
01078 applyRect(overlap, alpha));
01079 }
01080 };
01081
01082
01083
01084
01085
01086
01087
01088
01089
01090
01091
01092
01093
01094
01095
01096
01097
01098
01099
01100
01101
01102
01103
01104
01105
01106
01107
01108
01109
01110
01111
01112
01113
01114
01115
01116
01117
01118
01119
01120
01121
01122
01123
01124
01125
01126 #if 0
01127
01131 template <typename Remapper, typename OutputImage, typename Mask>
01132 void stitchToHDR(Remapper & remappers, OutputImage & panoImg, Mask & mask,
01133 AppBase::MultiProgressDisplay & progress)
01134 {
01135 std::vector<RemappedPanoImage<OutputImage, Mask> *> remapped;
01136
01137 for (UIntSet::const_iterator it = displayedImages.begin();
01138 it != displayedImages.end(); ++it)
01139 {
01140 remapped.push_back(remapper.getRemapped(pano, opts, *it, progress));
01141 }
01142 ReduceToHDRFunctor<RGBValue<float> > hdrmerge;
01143 reduceROIImages(remapped,
01144 destImageRange(panoImg), destImage(alpha),
01145 hdrmerge);
01146 #endif
01147
01148 template<typename ImageType, typename AlphaType>
01149 static void stitchPanoIntern(const PanoramaData & pano,
01150 const PanoramaOptions & opts,
01151 AppBase::MultiProgressDisplay & progress,
01152 const std::string & basename,
01153 UIntSet imgs)
01154 {
01155 using namespace vigra_ext;
01156
01157
01158
01159
01160 FileRemapper<ImageType, AlphaType> m;
01161
01162 switch (opts.outputFormat) {
01163 case PanoramaOptions::JPEG:
01164 case PanoramaOptions::PNG:
01165 case PanoramaOptions::TIFF:
01166 case PanoramaOptions::HDR:
01167 case PanoramaOptions::EXR:
01168 {
01169 if (opts.outputMode == PanoramaOptions::OUTPUT_HDR) {
01170 ReduceToHDRFunctor<typename ImageType::value_type> hdrmerge;
01171 ReduceStitcher<ImageType, AlphaType> stitcher(pano, progress);
01172 stitcher.stitch(opts, imgs, basename, m, hdrmerge);
01173 } else {
01174 WeightedStitcher<ImageType, AlphaType> stitcher(pano, progress);
01175 stitcher.stitch(opts, imgs, basename,
01176 m);
01177 }
01178 break;
01179 }
01180 case PanoramaOptions::TIFF_m:
01181 case PanoramaOptions::HDR_m:
01182 case PanoramaOptions::EXR_m:
01183 {
01184 MultiImageRemapper<ImageType, AlphaType> stitcher(pano, progress);
01185 stitcher.stitch(opts, imgs, basename,
01186 m);
01187 break;
01188 }
01189 case PanoramaOptions::TIFF_multilayer:
01190 {
01191 TiffMultiLayerRemapper<ImageType, AlphaType> stitcher(pano, progress);
01192 stitcher.stitch(opts, imgs, basename,
01193 m);
01194 break;
01195 }
01196 case PanoramaOptions::TIFF_mask:
01197 case PanoramaOptions::TIFF_multilayer_mask:
01198 DEBUG_ERROR("multi mask stitching not implemented!");
01199 break;
01200 default:
01201 DEBUG_ERROR("output format " << opts.getFormatName(opts.outputFormat) << "not supported");
01202 break;
01203 }
01204 }
01205
01212 IMPEX void stitchPanorama(const PanoramaData & pano,
01213 const PanoramaOptions & opts,
01214 AppBase::MultiProgressDisplay & progress,
01215 const std::string & basename,
01216 const UIntSet & usedImgs);
01217
01218
01219
01220
01221 void stitchPanoGray_8_16(const PanoramaData & pano,
01222 const PanoramaOptions & opts,
01223 AppBase::MultiProgressDisplay & progress,
01224 const std::string & basename,
01225 const UIntSet & usedImgs,
01226 const char * pixelType);
01227
01228 void stitchPanoGray_32_float(const PanoramaData & pano,
01229 const PanoramaOptions & opts,
01230 AppBase::MultiProgressDisplay & progress,
01231 const std::string & basename,
01232 const UIntSet & usedImgs,
01233 const char * pixelType);
01234
01235
01236 void stitchPanoRGB_8_16(const PanoramaData & pano,
01237 const PanoramaOptions & opts,
01238 AppBase::MultiProgressDisplay & progress,
01239 const std::string & basename,
01240 const UIntSet & usedImgs,
01241 const char * pixelType);
01242
01243 void stitchPanoRGB_32_float(const PanoramaData & pano,
01244 const PanoramaOptions & opts,
01245 AppBase::MultiProgressDisplay & progress,
01246 const std::string & basename,
01247 const UIntSet & usedImgs,
01248 const char * pixelType);
01249
01250 }
01251 }
01252
01253 #endif // _H