[an error occurred while processing this directive]
Main Page | Modules | Namespace List | Class Hierarchy | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

hugin_base/nona/Stitcher.h

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 -*-
00026 #ifndef _NONA_STITCHER_H
00027 #define _NONA_STITCHER_H
00028 
00029 // To avoid windows.h namespace pollution later on
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 // calculate distance image for multi file output
00057 #define STITCHER_CALC_DIST_IMG 0
00058 
00059 // somehow these are still
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         // create transforms
00096         //    SpaceTransform t;
00097         //    SpaceTransform invT;
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         // calculate ROI for this image.
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     // write TIFF header
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     //    template<typename ImgIter, typename ImgAccessor>
00177 
00178     //void stitch(const PanoramaOptions & opts, UIntSet & images, vigra::triple<ImgIter, ImgIter, ImgAccessor> output);
00179 
00180 protected:
00181     // calculate ROI's if not already known
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         // setup the output.
00224         prepareOutputFile(opts);
00225 
00226         unsigned int nImg = images.size();
00227         Base::m_progress.pushTask(AppBase::ProgressTask("Remapping", "", 1.0/(nImg)));
00228         // remap each image and save
00229         int i=0;
00230         for (UIntSet::const_iterator it = images.begin();
00231              it != images.end(); ++it)
00232         {
00233             // get a remapped image.
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                 // this can be thrown, if an image
00241                 // is completely out of the pano
00242                 std::cerr << e.what();
00243             }
00244             // free remapped image
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             // do not save empty files...
00269             // TODO: need to tell other parts (enblend etc.) about it too!
00270             return;
00271 
00272         if (opts.outputMode == PanoramaOptions::OUTPUT_HDR) {
00273             // export alpha channel as gray channel (original pixel weights)
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();
00280                 newOutRect.moveBy(-opts.getROI().upperLeft());
00281                 vigra::copyImage(vigra_ext::applyRect(remapped.boundingBox(),
00282                                  vigra_ext::srcMaskRange(remapped)),
00283                                  vigra_ext::applyRect(newOutRect,
00284                                  destImage(alpha)));
00285                 vigra::exportImage(srcImageRange(alpha), exinfo1);
00286             } else {
00287                 exinfo1.setPosition(remapped.boundingBox().upperLeft());
00288                 exinfo1.setCanvasSize(vigra::Size2D(opts.getWidth(), opts.getHeight()));
00289                 vigra::exportImage(srcImageRange(remapped.m_mask), exinfo1);
00290             }
00291 
00292             // calculate real alpha for saving with the image
00293             Base::m_progress.setMessage("Calculating mask");
00294             remapped.calcAlpha();
00295         }
00296 
00297 
00298         if (! opts.tiff_saveROI) {
00299             // FIXME: this is stupid. Should not require space for full image...
00300             // but this would need a lower level interface in vigra impex
00301             complete.resize(opts.getROI().size());
00302             alpha.resize(opts.getROI().size());
00303             vigra::Rect2D newOutRect = remapped.boundingBox();
00304             newOutRect.moveBy(-opts.getROI().upperLeft());
00305             vigra::copyImage(vigra_ext::applyRect(remapped.boundingBox(),
00306                                  vigra_ext::srcImageRange(remapped)),
00307                              vigra_ext::applyRect(newOutRect,
00308                                  destImage(complete)));
00309 
00310             vigra::copyImage(vigra_ext::applyRect(remapped.boundingBox(),
00311                                  vigra_ext::srcMaskRange(remapped)),
00312                              vigra_ext::applyRect(newOutRect,
00313                                  destImage(alpha)));
00314             final_img = & complete;
00315             alpha_img = & alpha;
00316         } else {
00317             final_img = &remapped.m_image;
00318             alpha_img = &remapped.m_mask;
00319         }
00320 
00321 /*
00322         std::string cext = hugin_utils::getExtension(m_basename);
00323         std::transform(cext.begin(),cext.end(), cext.begin(), (int(*)(int))std::tolower);
00324         // remove extension only if it specifies the same file type, otherwise
00325         // its probably part of the filename.
00326         if (cext == ext) {
00327             m_basename = hugin_utils::stripExtension(m_basename);
00328         }
00329 */
00330         std::string ext = opts.getOutputExtension();
00331         std::ostringstream filename;
00332         filename << m_basename << std::setfill('0') << std::setw(4) << imgNr << "." + ext;
00333 
00334 
00335         Base::m_progress.setMessage(std::string("saving ") +
00336                 hugin_utils::stripPath(filename.str()));
00337 
00338         vigra::ImageExportInfo exinfo(filename.str().c_str());
00339         exinfo.setXResolution(150);
00340         exinfo.setYResolution(150);
00341         if (opts.tiff_saveROI) {
00342             exinfo.setPosition(remapped.boundingBox().upperLeft());
00343             exinfo.setCanvasSize(vigra::Size2D(opts.getWidth(), opts.getHeight()));
00344         } else {
00345             exinfo.setPosition(opts.getROI().upperLeft());
00346             exinfo.setCanvasSize(vigra::Size2D(opts.getWidth(), opts.getHeight()));
00347         }
00348         if (opts.outputPixelType.size() > 0) {
00349             exinfo.setPixelType(opts.outputPixelType.c_str());
00350         }
00351         if (ext == "tif") {
00352             exinfo.setCompression(opts.tiffCompression.c_str());
00353         }
00354 
00355         vigra::exportImageAlpha(srcImageRange(*final_img), srcImage(*alpha_img),
00356                                     exinfo);
00357 
00358         if (opts.saveCoordImgs) {
00359             vigra::UInt16Image xImg;
00360             vigra::UInt16Image yImg;
00361 
00362             Base::m_progress.setMessage("creating coordinate images");
00363 
00364             remapped.calcSrcCoordImgs(xImg, yImg);
00365             vigra::UInt16Image dist;
00366             if (! opts.tiff_saveROI) {
00367                 dist.resize(opts.getWidth(), opts.getHeight());
00368                 vigra::copyImage(srcImageRange(xImg),
00369                                 vigra_ext::applyRect(remapped.boundingBox(),
00370                                                     destImage(dist)));
00371                 std::ostringstream filename2;
00372                 filename2 << m_basename << std::setfill('0') << std::setw(4) << imgNr << "_x.tif";
00373 
00374                 vigra::ImageExportInfo exinfo(filename2.str().c_str());
00375                 exinfo.setXResolution(150);
00376                 exinfo.setYResolution(150);
00377                 exinfo.setCompression(opts.tiffCompression.c_str());
00378                 vigra::exportImage(srcImageRange(dist), exinfo);
00379 
00380 
00381                 vigra::copyImage(srcImageRange(yImg),
00382                                 vigra_ext::applyRect(remapped.boundingBox(),
00383                                         destImage(dist)));
00384                 std::ostringstream filename3;
00385                 filename3 << m_basename << std::setfill('0') << std::setw(4) << imgNr << "_y.tif";
00386 
00387                 vigra::ImageExportInfo exinfo2(filename3.str().c_str());
00388                 exinfo2.setXResolution(150);
00389                 exinfo2.setYResolution(150);
00390                 exinfo2.setCompression(opts.tiffCompression.c_str());
00391                 vigra::exportImage(srcImageRange(dist), exinfo2);
00392             } else {
00393                 std::ostringstream filename2;
00394                 filename2 << m_basename << std::setfill('0') << std::setw(4) << imgNr << "_x.tif";
00395                 vigra::ImageExportInfo exinfo(filename2.str().c_str());
00396                 exinfo.setXResolution(150);
00397                 exinfo.setYResolution(150);
00398                 exinfo.setCompression(opts.tiffCompression.c_str());
00399                 vigra::exportImage(srcImageRange(xImg), exinfo);
00400                 std::ostringstream filename3;
00401                 filename3 << m_basename << std::setfill('0') << std::setw(4) << imgNr << "_y.tif";
00402                 vigra::ImageExportInfo exinfo2(filename3.str().c_str());
00403                 exinfo2.setXResolution(150);
00404                 exinfo2.setYResolution(150);
00405                 exinfo2.setCompression(opts.tiffCompression.c_str());
00406                 vigra::exportImage(srcImageRange(yImg), exinfo2);
00407             }
00408         }
00409     }
00410 
00411     virtual void finalizeOutputFile(const PanoramaOptions & opts)
00412     {
00413     }
00414 
00415 protected:
00416     std::string m_basename;
00417 };
00418 
00419 
00420 
00422 template <typename ImageType, typename AlphaImageType>
00423 class TiffMultiLayerRemapper : public MultiImageRemapper<ImageType, AlphaImageType>
00424 {
00425 public:
00426 
00427     typedef MultiImageRemapper<ImageType, AlphaImageType> Base;
00428     TiffMultiLayerRemapper(const PanoramaData & pano,
00429                            AppBase::MultiProgressDisplay & progress)
00430         : MultiImageRemapper<ImageType, AlphaImageType> (pano, progress)
00431     {
00432     }
00433 
00434     virtual ~TiffMultiLayerRemapper()
00435     {
00436     }
00437 
00438     virtual void prepareOutputFile(const PanoramaOptions & opts)
00439     {
00440         std::string filename = Base::m_basename + ".tif";
00441         DEBUG_DEBUG("Layering image into a multi image tif file " << filename);
00442         m_tiff = TIFFOpen(filename.c_str(), "w");
00443         DEBUG_ASSERT(m_tiff && "could not open tiff output file");
00444     }
00445 
00447     virtual void saveRemapped(RemappedPanoImage<ImageType, AlphaImageType> & remapped,
00448                               unsigned int imgNr, unsigned int nImg,
00449                               const PanoramaOptions & opts)
00450     {
00451         if (remapped.boundingBox().isEmpty())
00452            return;
00453 
00454         DEBUG_DEBUG("Saving TIFF ROI");
00455         vigra_ext::createTiffDirectory(m_tiff,
00456                                        Base::m_pano.getImage(imgNr).getFilename(),
00457                                        Base::m_basename,
00458                                        opts.tiffCompression,
00459                                        imgNr+1, nImg, remapped.boundingBox().upperLeft(),
00460                                        opts.getROI().size(),
00461                                        remapped.m_ICCProfile);
00462         vigra_ext::createAlphaTiffImage(vigra::srcImageRange(remapped.m_image),
00463                                         vigra::maskImage(remapped.m_mask),
00464                                         m_tiff);
00465         TIFFFlush(m_tiff);
00466     }
00467 
00469     virtual void finalizeOutputFile(const PanoramaOptions & opts)
00470     {
00471         TIFFClose(m_tiff);
00472     }
00473 
00474 
00475 protected:
00476     vigra::TiffImage * m_tiff;
00477 };
00478 
00479 
00480 typedef std::vector<std::pair<float, unsigned int> > AlphaVector;
00481 
00485 struct CalcMaskUnion
00486 {
00487     CalcMaskUnion()
00488         : count(0)
00489     { }
00490 
00491     template<typename PIXEL>
00492     PIXEL operator()(PIXEL const & img1, PIXEL const & img2)
00493     {
00494         if (img1 > vigra::NumericTraits<PIXEL>::zero() && img2 > vigra::NumericTraits<PIXEL>::zero()) {
00495             count++;
00496             return img1;
00497         } else {
00498             return vigra::NumericTraits<PIXEL>::zero();
00499         }
00500     }
00501 
00502     int count;
00503 };
00504 
00505 
00506 template <typename ImageType, typename AlphaType>
00507 class WeightedStitcher : public Stitcher<ImageType, AlphaType>
00508 {
00509 public:
00510 
00511     typedef Stitcher<ImageType, AlphaType> Base;
00512     WeightedStitcher(const PanoramaData & pano,
00513                      AppBase::MultiProgressDisplay & progress)
00514         : Stitcher<ImageType, AlphaType>(pano, progress)
00515     {
00516     }
00517 
00518     virtual ~WeightedStitcher() {};
00519 
00520     template<class ImgIter, class ImgAccessor,
00521              class AlphaIter, class AlphaAccessor>
00522     void stitch(const PanoramaOptions & opts, UIntSet & imgSet,
00523                         vigra::triple<ImgIter, ImgIter, ImgAccessor> pano,
00524                         std::pair<AlphaIter, AlphaAccessor> alpha,
00525                         SingleImageRemapper<ImageType, AlphaType> & remapper)
00526     {
00527         std::vector<unsigned int> images;
00528         // calculate stitching order
00529         estimateBlendingOrder(Base::m_pano, imgSet, images);
00530 
00531         unsigned int nImg = images.size();
00532 
00533         Base::m_progress.pushTask(AppBase::ProgressTask("Stitching", "", 1.0/(nImg)));  
00534         // empty ROI
00535         vigra::Rect2D panoROI;
00536 
00537         int i=0;
00538         // remap each image and blend into main pano image
00539         for (UIntVector::const_iterator it = images.begin();
00540              it != images.end(); ++it)
00541         {
00542             // get a remapped image.
00543             DEBUG_DEBUG("remapping image: " << *it);
00544             RemappedPanoImage<ImageType, AlphaType> *
00545             remapped = remapper.getRemapped(Base::m_pano, opts, *it,
00546                                             Base::m_rois[i], Base::m_progress);
00547             Base::m_progress.setMessage("blending");
00548             // add image to pano and panoalpha, adjusts panoROI as well.
00549             try {
00550                 vigra_ext::blend(*remapped, pano, alpha, panoROI,
00551                                  Base::m_progress);
00552                 // update bounding box of the panorama
00553                 panoROI = panoROI | remapped->boundingBox();
00554             } catch (vigra::PreconditionViolation & e) {
00555                 DEBUG_ERROR("exception during stitching" << e.what());
00556                 // this can be thrown, if an image
00557                 // is completely out of the pano
00558             }
00559             // free remapped image
00560             remapper.release(remapped);
00561             i++;
00562         }
00563     }
00564 
00565     void stitch(const PanoramaOptions & opts, UIntSet & imgSet,
00566                         const std::string & filename,
00567                         SingleImageRemapper<ImageType, AlphaType> & remapper)
00568     {
00569         Base::stitch(opts, imgSet, filename, remapper);
00570 
00571         std::string basename = filename;
00572 
00573         // create panorama canvas
00574         ImageType pano(opts.getWidth(), opts.getHeight());
00575         AlphaType panoMask(opts.getWidth(), opts.getHeight());
00576 
00577         stitch(opts, imgSet, vigra::destImageRange(pano), vigra::destImage(panoMask), remapper);
00578         
00579             std::string ext = opts.getOutputExtension();
00580         std::string cext = hugin_utils::getExtension(basename);
00581         std::transform(cext.begin(),cext.end(), cext.begin(), (int(*)(int))std::tolower);
00582         // remove extension only if it specifies the same file type, otherwise
00583         // its probably part of the filename.
00584         if (cext == ext) {
00585             basename = hugin_utils::stripExtension(basename);
00586         }
00587         std::string outputfile = basename + "." + ext;
00588         
00589         // save the remapped image
00590         Base::m_progress.setMessage("saving result: " + hugin_utils::stripPath(outputfile));
00591         DEBUG_DEBUG("Saving panorama: " << outputfile);
00592         vigra::ImageExportInfo exinfo(outputfile.c_str());
00593         exinfo.setXResolution(150);
00594         exinfo.setYResolution(150);
00595         exinfo.setICCProfile(iccProfile);
00596         if (opts.outputPixelType.size() > 0) {
00597             exinfo.setPixelType(opts.outputPixelType.c_str());
00598         }
00599 
00600         // set compression quality for jpeg images.
00601         if (opts.outputFormat == PanoramaOptions::JPEG) {
00602             char jpgCompr[4];
00603             snprintf(jpgCompr,4,"%d", opts.quality);
00604             exinfo.setCompression(jpgCompr);
00605             vigra::exportImage(srcImageRange(pano), exinfo);
00606         } else if (opts.outputFormat == PanoramaOptions::TIFF) {
00607             exinfo.setCompression(opts.tiffCompression.c_str());
00608             vigra::exportImageAlpha(srcImageRange(pano),
00609                                            srcImage(panoMask), exinfo);
00610         } else if (opts.outputFormat == PanoramaOptions::HDR) {
00611             exinfo.setPixelType("FLOAT");
00612             vigra::exportImage(srcImageRange(pano), exinfo);
00613         } else {
00614             vigra::exportImageAlpha(srcImageRange(pano),
00615                                            srcImage(panoMask), exinfo);
00616         }
00617         /*
00618 #ifdef DEBUG
00619         vigra::exportImage(srcImageRange(panoMask), vigra::ImageExportInfo("pano_alpha.tif"));
00620 #endif
00621         */
00622         Base::m_progress.popTask();
00623 
00624     }
00625 
00626 protected:
00627     vigra::ImageImportInfo::ICCProfile iccProfile;
00628 };
00629 
00630 
00632 template<class VALUETYPE>
00633 struct ReduceToDifferenceFunctor
00634 {
00635     typedef VALUETYPE  argument_type;
00636     typedef VALUETYPE  result_type;
00637     typedef typename vigra::NumericTraits<argument_type> Traits;
00638     typedef typename Traits::RealPromote float_type;
00639 
00640 
00641     ReduceToDifferenceFunctor()
00642     {
00643         reset();
00644     }
00645 
00646     void reset()
00647     {
00648         sum = vigra::NumericTraits<float_type>::zero();
00649         values.clear();
00650     }
00651 
00652     template<class T, class M> 
00653     void operator() (T const &v, M const &a)
00654     {
00655         if (a) {
00656             values.push_back(v);
00657             sum = sum + v;
00658         }
00659     }
00660 
00662     float_type operator()() const
00663     {
00664         typedef typename std::vector<float_type>::const_iterator Iter;
00665         if (values.size() > 1) {
00666             float_type mean = sum / values.size();
00667             float_type error = vigra::NumericTraits<float_type>::zero();
00668             for (Iter it= values.begin();  it != values.end(); ++it) {
00669                 error +=  vigra::abs(*it-mean);
00670             }
00671             return error;
00672         } else {
00673             return sum;
00674         }
00675     }
00676     std::vector<float_type> values;
00677     float_type sum;
00678 };
00679 
00684 template <typename ImageType, typename AlphaType>
00685 class ReduceStitcher : public Stitcher<ImageType, AlphaType>
00686 {
00687     typedef Stitcher<ImageType, AlphaType> Base;
00688 public:
00689     ReduceStitcher(const PanoramaData & pano,
00690                    AppBase::MultiProgressDisplay & progress)
00691     : Stitcher<ImageType, AlphaType>(pano, progress)
00692     {
00693     }
00694 
00695     virtual ~ReduceStitcher()
00696     {
00697     }
00698 
00699     template <class FUNCTOR>
00700     void stitch(const PanoramaOptions & opts, UIntSet & imgSet,
00701                 const std::string & filename,
00702                 SingleImageRemapper<ImageType, AlphaType> & remapper,
00703                 FUNCTOR & reduce)
00704     {
00705         Base::stitch(opts, imgSet, filename, remapper);
00706 
00707         std::string basename = filename;
00708 
00709     // create panorama canvas
00710         ImageType pano(opts.getWidth(), opts.getHeight());
00711         AlphaType panoMask(opts.getWidth(), opts.getHeight());
00712 
00713         stitch(opts, imgSet, vigra::destImageRange(pano), vigra::destImage(panoMask),
00714                remapper, reduce);
00715 
00716         std::string ext = opts.getOutputExtension();
00717         std::string cext = hugin_utils::getExtension(basename);
00718         std::transform(cext.begin(),cext.end(), cext.begin(), (int(*)(int))std::tolower);
00719         // remove extension only if it specifies the same file type, otherwise
00720         // its probably part of the filename.
00721         if (cext == ext) {
00722             basename = hugin_utils::stripExtension(basename);
00723         }
00724         std::string outputfile = basename + "." + ext;
00725 
00726 //        Base::m_progress.setMessage("saving result: " + hugin_utils::stripPath(outputfile));
00727         DEBUG_DEBUG("Saving panorama: " << outputfile);
00728         vigra::ImageExportInfo exinfo(outputfile.c_str());
00729         if (opts.outputPixelType.size() > 0) {
00730             exinfo.setPixelType(opts.outputPixelType.c_str());
00731         }
00732         exinfo.setXResolution(150);
00733         exinfo.setYResolution(150);
00734 //        exinfo.setICCProfile(iccProfile);
00735         // set compression quality for jpeg images.
00736         if (opts.outputFormat == PanoramaOptions::JPEG) {
00737             char jpgCompr[4];
00738             snprintf(jpgCompr,4,"%d", opts.quality);
00739             exinfo.setCompression(jpgCompr);
00740             vigra::exportImage(srcImageRange(pano), exinfo);
00741         } else if (opts.outputFormat == PanoramaOptions::TIFF) {
00742             exinfo.setCompression(opts.tiffCompression.c_str());
00743             vigra::exportImageAlpha(srcImageRange(pano),
00744                                     srcImage(panoMask), exinfo);
00745         } else if (opts.outputFormat == PanoramaOptions::HDR) {
00746             vigra::exportImage(srcImageRange(pano), exinfo);
00747         } else {
00748             vigra::exportImageAlpha(srcImageRange(pano),
00749                                     srcImage(panoMask), exinfo);
00750         }
00751         /*
00752 #ifdef DEBUG
00753         vigra::exportImage(srcImageRange(panoMask), vigra::ImageExportInfo("pano_alpha.tif"));
00754 #endif
00755         */
00756     }
00757 
00758     
00759     template<class ImgIter, class ImgAccessor,
00760              class AlphaIter, class AlphaAccessor,
00761              class FUNCTOR>
00762     void stitch(const PanoramaOptions & opts, UIntSet & imgSet,
00763                 vigra::triple<ImgIter, ImgIter, ImgAccessor> pano,
00764                 std::pair<AlphaIter, AlphaAccessor> alpha,
00765                 SingleImageRemapper<ImageType, AlphaType> & remapper,
00766                 FUNCTOR & reduce)
00767     {
00768         typedef typename
00769             vigra::NumericTraits<typename ImageType::value_type> Traits;
00770         typedef typename
00771             Traits::RealPromote RealImgType;
00772         typedef typename ImageType::value_type ImgType;
00773         typedef typename AlphaAccessor::value_type MaskType;
00774 
00775         Base::stitch(opts, imgSet, "dummy", remapper);
00776 
00777         // remap all images..
00778         typedef std::vector<RemappedPanoImage<ImageType, AlphaType> *> RemappedVector;
00779         unsigned int nImg = imgSet.size();
00780 
00781         Base::m_progress.pushTask(AppBase::ProgressTask("Stitching", "", 1.0/(nImg)));  
00782         // empty ROI
00783         //      vigra::Rect2D panoROI;
00784         // remap all images..
00785         RemappedVector remapped(nImg);
00786 
00787         int i=0;
00788         // remap each image and blend into main pano image
00789         for (UIntSet::const_iterator it = imgSet.begin();
00790                 it != imgSet.end(); ++it)
00791         {
00792             // get a copy of the remapped image.
00793             // not very good, keeps all images in memory,
00794             // but should be enought for the preview.
00795             remapped[i] = remapper.getRemapped(Base::m_pano, opts, *it,
00796                                                Base::m_rois[i], Base::m_progress);
00797             i++;
00798         }
00799         vigra::Diff2D size =  pano.second - pano.first;
00800         ImgIter output = pano.first;
00801         // iterate over the whole image...
00802         // calculate something on the pixels that belong together..
00803         for (int y=0; y < size.y; y++) {
00804             for (int x=0; x < size.x; x++) {
00805                 reduce.reset();
00806                 MaskType maskRes=0;
00807                 for (unsigned int i=0; i< nImg; i++) {
00808                     MaskType a = remapped[i]->getMask(x,y);
00809                     if (a) {
00810                         maskRes = vigra_ext::LUTTraits<MaskType>::max();
00811                         reduce(remapped[i]->operator()(x,y), a);
00812                     }
00813                 }
00814                 pano.third.set(Traits::fromRealPromote(reduce()), output, vigra::Diff2D(x,y));
00815                 alpha.second.set(maskRes, alpha.first, vigra::Diff2D(x,y));
00816             }
00817         }
00818         Base::m_progress.popTask();
00819 
00820         for (typename RemappedVector::iterator it=remapped.begin();
00821              it != remapped.end(); ++it)
00822         {
00823             remapper.release(*it);
00824         }
00825     }
00826 };
00827 
00830 template <typename ImageType, typename AlphaType>
00831 class SimpleStitcher : public Stitcher<ImageType, AlphaType>
00832 {
00833     typedef Stitcher<ImageType, AlphaType> Base;
00834 public:
00835     SimpleStitcher(const PanoramaData & pano,
00836                      AppBase::MultiProgressDisplay & progress)
00837         : Stitcher<ImageType, AlphaType>(pano, progress)
00838     {
00839     }
00840 
00841     virtual ~SimpleStitcher()
00842     {
00843     }
00844 
00845     template<class ImgIter, class ImgAccessor,
00846              class AlphaIter, class AlphaAccessor,
00847              class BlendFunctor>
00848     void stitch(const PanoramaOptions & opts, UIntSet & imgSet,
00849                 vigra::triple<ImgIter, ImgIter, ImgAccessor> pano,
00850                 std::pair<AlphaIter, AlphaAccessor> alpha,
00851                 SingleImageRemapper<ImageType, AlphaType> & remapper,
00852                 BlendFunctor & blend)
00853     {
00854         Base::m_images=imgSet;
00855         Base::calcOutputROIS(opts, imgSet);
00856 
00857         unsigned int nImg = imgSet.size();
00858 
00859         Base::m_progress.pushTask(AppBase::ProgressTask("Stitching", "", 1.0/(nImg)));
00860         // empty ROI
00861         vigra::Rect2D panoROI;
00862 
00863             unsigned i=0;
00864         // remap each image and blend into main pano image
00865         for (UIntSet::reverse_iterator it = imgSet.rbegin();
00866             it != imgSet.rend(); ++it)
00867         {
00868             // get a remapped image.
00869             RemappedPanoImage<ImageType, AlphaType> *
00870             remapped = remapper.getRemapped(Base::m_pano, opts, *it,
00871                                             Base::m_rois[i], Base::m_progress);
00872             if (iccProfile.size() > 0) {
00873                 // try to extract icc profile.
00874                 iccProfile = remapped->m_ICCProfile;
00875             }
00876             Base::m_progress.setMessage("blending");
00877             // add image to pano and panoalpha, adjusts panoROI as well.
00878             try {
00879                 blend(*remapped, pano, alpha, panoROI);
00880                 // update bounding box of the panorama
00881                 panoROI = panoROI | remapped->boundingBox();
00882             } catch (vigra::PreconditionViolation & e) {
00883                 // this can be thrown, if an image
00884                 // is completely out of the pano
00885                                 std::cerr << e.what();
00886             }
00887             // free remapped image
00888             remapper.release(remapped);
00889             i++;
00890         }
00891         Base::m_progress.popTask();
00892     }
00893 
00894     template <class BlendFunctor>
00895     void stitch(const PanoramaOptions & opts, UIntSet & imgSet,
00896                 const std::string & filename,
00897                 SingleImageRemapper<ImageType, AlphaType> & remapper,
00898                 BlendFunctor & blend)
00899     {
00900         std::string basename = filename;
00901 
00902         // create panorama canvas
00903         ImageType pano(opts.getWidth(), opts.getHeight());
00904         AlphaType panoMask(opts.getWidth(), opts.getHeight());
00905 
00906         stitch(opts, imgSet, vigra::destImageRange(pano), vigra::destImage(panoMask), remapper, blend);
00907         
00908             std::string ext = opts.getOutputExtension();
00909         std::string cext = hugin_utils::getExtension(basename);
00910         std::transform(cext.begin(),cext.end(), cext.begin(), (int(*)(int))std::tolower);
00911         // remove extension only if it specifies the same file type, otherwise
00912         // its probably part of the filename.
00913         if (cext == ext) {
00914             basename = hugin_utils::stripExtension(basename);
00915         }
00916         std::string outputfile = basename + "." + ext;
00917         
00918         Base::m_progress.setMessage("saving result: " + hugin_utils::stripPath(outputfile));
00919         DEBUG_DEBUG("Saving panorama: " << outputfile);
00920         vigra::ImageExportInfo exinfo(outputfile.c_str());
00921         exinfo.setXResolution(150);
00922         exinfo.setYResolution(150);
00923         exinfo.setICCProfile(iccProfile);
00924         // set compression quality for jpeg images.
00925         if (opts.outputFormat == PanoramaOptions::JPEG) {
00926             char jpgCompr[4];
00927             snprintf(jpgCompr,4,"%d", opts.quality);
00928             exinfo.setCompression(jpgCompr);
00929             vigra::exportImage(srcImageRange(pano), exinfo);
00930         } else if (opts.outputFormat == PanoramaOptions::TIFF) {
00931             exinfo.setCompression("DEFLATE");
00932             vigra::exportImageAlpha(srcImageRange(pano),
00933                                            srcImage(panoMask), exinfo);
00934         } else {
00935             vigra::exportImageAlpha(srcImageRange(pano),
00936                                            srcImage(panoMask), exinfo);
00937         }
00938         /*
00939 #ifdef DEBUG
00940         vigra::exportImage(srcImageRange(panoMask), vigra::ImageExportInfo("pano_alpha.tif"));
00941 #endif
00942         */
00943         Base::m_progress.popTask();
00944 
00945     }
00946 protected:
00947     vigra::ImageExportInfo::ICCProfile iccProfile;
00948 };
00949 
00953 struct StackingBlender
00954 {
00955 
00956 public:
00961     template <typename ImageType, typename AlphaType,
00962               typename PanoIter, typename PanoAccessor,
00963               typename AlphaIter, typename AlphaAccessor>
00964     void operator()(RemappedPanoImage<ImageType, AlphaType> & img,
00965                     vigra::triple<PanoIter, PanoIter, PanoAccessor> pano,
00966                     std::pair<AlphaIter, AlphaAccessor> alpha,
00967                     const vigra::Rect2D & panoROI)
00968     {
00969         using namespace vigra_ext;
00970     
00971         DEBUG_DEBUG("pano roi: " << panoROI << " img roi: " << img.boundingBox());
00972             typedef typename AlphaIter::value_type AlphaValue;
00973 
00974 //        DEBUG_DEBUG("no overlap, copying upper area. imgroi " << img.roi());
00975 //        DEBUG_DEBUG("pano roi: " << panoROI.upperLeft() << " -> " << panoROI.lowerRight());
00976         DEBUG_DEBUG("size of panorama: " << pano.second - pano.first);
00977 
00978                 // check if bounding box of image is outside of panorama...
00979                 vigra::Rect2D fullPano(vigra::Size2D(pano.second-pano.first));
00980         // blend only the intersection (which is inside the pano..)
00981         vigra::Rect2D overlap = fullPano & img.boundingBox();
00982 
00983         vigra::copyImageIf(applyRect(overlap, vigra_ext::srcImageRange(img)),
00984                            applyRect(overlap, vigra_ext::srcMask(img)),
00985                            applyRect(overlap, std::make_pair(pano.first, pano.third)));
00986         // copy mask
00987         vigra::copyImageIf(applyRect(overlap, srcMaskRange(img)),
00988                            applyRect(overlap, srcMask(img)),
00989                            applyRect(overlap, alpha));
00990     }
00991 };
00992 
00994 struct SeamBlender
00995 {
00996 public:
00997 
01002     template <typename ImageType, typename AlphaType,
01003               typename PanoIter, typename PanoAccessor,
01004               typename AlphaIter, typename AlphaAccessor>
01005     void operator()(RemappedPanoImage<ImageType, AlphaType> & img,
01006                     vigra::triple<PanoIter, PanoIter, PanoAccessor> pano,
01007                     std::pair<AlphaIter, AlphaAccessor> alpha,
01008                     const vigra::Rect2D & panoROI)
01009     {
01010         DEBUG_DEBUG("pano roi: " << panoROI << " img roi: " << img.boundingBox());
01011         AppBase::MultiProgressDisplay dummy;
01012         vigra_ext::blend(img, pano, alpha, panoROI, dummy);
01013     }
01014 };
01015 
01017 struct DifferenceBlender
01018 {
01019 public:
01020 
01025     template <typename ImageType, typename AlphaType,
01026               typename PanoIter, typename PanoAccessor,
01027               typename AlphaIter, typename AlphaAccessor>
01028     void operator()(RemappedPanoImage<ImageType, AlphaType> & img,
01029                     vigra::triple<PanoIter, PanoIter, PanoAccessor> pano,
01030                     std::pair<AlphaIter, AlphaAccessor> alpha,
01031                     const vigra::Rect2D & panoROI)
01032     {
01033                 DEBUG_DEBUG("pano roi: " << panoROI << " img roi: " << img.boundingBox());
01034                 typedef typename AlphaIter::value_type AlphaValue;
01035 
01036                 // check if bounding box of image is outside of panorama...
01037                 vigra::Rect2D fullPano(vigra::Size2D(pano.second-pano.first));
01038         // blend only the intersection (which is inside the pano..)
01039         vigra::Rect2D overlap = fullPano & img.boundingBox();
01040 
01041         vigra::combineTwoImagesIf(applyRect(overlap, vigra_ext::srcImageRange(img)),
01042                                   applyRect(overlap, std::make_pair(pano.first, pano.third)),
01043                                   applyRect(overlap, vigra_ext::srcMask(img)),
01044                                   applyRect(overlap, std::make_pair(pano.first, pano.third)),
01045                                   abs(vigra::functor::Arg1()-vigra::functor::Arg2()));
01046 
01047         // copy mask
01048         vigra::copyImageIf(applyRect(overlap, srcMaskRange(img)),
01049                            applyRect(overlap, srcMask(img)),
01050                            applyRect(overlap, alpha));
01051     }
01052 };
01053 
01054 /*
01055 template <typename ImageType, typename AlphaImageType>
01056 class MultiResStitcher : public WeightedSticher<ImageType, AlphaImageType>
01057 {
01058 public:
01059     MultiResStitcher(const PanoramaData & pano,
01060                      AppBase::MultiProgressDisplay & progress)
01061         : WeightedStitcher(pano, progress)
01062     {
01063     }
01064 
01065     template <typename PanoIter, typename PanoAccessor,
01066               typename MaskIter, typename MaskAccessor>
01067     virtual void blendOverlap(vigra::triple<PanoIter, PanoIter, PanoAccessor> image,
01068                       std::pair<MaskIter, MaskAccessor> imageMask,
01069                       std::pair<PanoIter, PanoAccessor> pano,
01070                       std::pair<MaskIter, MaskAccessor> panoMask)
01071     {
01072         vigra::Diff2D size = image.second - image.first;
01073 
01074         // create new blending masks
01075         vigra::BasicImage<typename MaskIter::value_type> blendPanoMask(size);
01076         vigra::BasicImage<typename MaskIter::value_type> blendImageMask(size);
01077 
01078         // calculate the stitching masks.
01079         vigra_ext::nearestFeatureTransform(srcIterRange(panoMask.first, panoMask.first + size),
01080                                            imageMask,
01081                                            destImage(blendPanoMask),
01082                                            destImage(blendImageMask),
01083                                            m_progress);
01084 
01085         // calculate the a number of
01086 
01087         // copy the image into the panorama
01088         vigra::copyImageIf(image, maskImage(blendImageMask), pano);
01089         // copy mask
01090         vigra::copyImageIf(srcImageRange(blendImageMask), maskImage(blendImageMask), panoMask);
01091     }
01092 
01093 private:
01094 };
01095 */
01096 
01097 
01098 #if 0
01099 
01103 template <typename Remapper, typename OutputImage, typename Mask>
01104 void stitchToHDR(Remapper & remappers, OutputImage & panoImg, Mask & mask, 
01105                  AppBase::MultiProgressDisplay & progress)
01106 {
01107     std::vector<RemappedPanoImage<OutputImage, Mask> *> remapped;
01108     // get all remapped images
01109     for (UIntSet::const_iterator it = displayedImages.begin();
01110         it != displayedImages.end(); ++it)
01111     {
01112         remapped.push_back(remapper.getRemapped(pano, opts, *it, progress));
01113     }
01114     ReduceToHDRFunctor<RGBValue<float> > hdrmerge;
01115     reduceROIImages(remapped,
01116                     destImageRange(panoImg), destImage(alpha),
01117                     hdrmerge);
01118 #endif
01119 
01120 template<typename ImageType, typename AlphaType>
01121 static void stitchPanoIntern(const PanoramaData & pano,
01122                              const PanoramaOptions & opts,
01123                              AppBase::MultiProgressDisplay & progress,
01124                              const std::string & basename,
01125                              UIntSet imgs)
01126 {
01127     using namespace vigra_ext;
01128     
01129     //    typedef
01130     //        vigra::NumericTraits<typename OutputImageType::Accessor::value_type> DestTraits;
01131 
01132     FileRemapper<ImageType, AlphaType> m;
01133     // determine stitching output
01134     switch (opts.outputFormat) {
01135         case PanoramaOptions::JPEG:
01136         case PanoramaOptions::PNG:
01137         case PanoramaOptions::TIFF:
01138         case PanoramaOptions::HDR:
01139         case PanoramaOptions::EXR:
01140         {
01141             if (opts.outputMode == PanoramaOptions::OUTPUT_HDR) {
01142                 ReduceToHDRFunctor<typename ImageType::value_type> hdrmerge;
01143                 ReduceStitcher<ImageType, AlphaType> stitcher(pano, progress);
01144                 stitcher.stitch(opts, imgs, basename, m, hdrmerge);
01145             } else {
01146                 WeightedStitcher<ImageType, AlphaType> stitcher(pano, progress);
01147                 stitcher.stitch(opts, imgs, basename,
01148                                 m);
01149             }
01150             break;
01151         }
01152         case PanoramaOptions::TIFF_m:
01153         case PanoramaOptions::HDR_m:
01154         case PanoramaOptions::EXR_m:
01155         {
01156             MultiImageRemapper<ImageType, AlphaType> stitcher(pano, progress);
01157             stitcher.stitch(opts, imgs, basename,
01158                             m);
01159             break;
01160         }
01161         case PanoramaOptions::TIFF_multilayer:
01162         {
01163             TiffMultiLayerRemapper<ImageType, AlphaType> stitcher(pano, progress);
01164             stitcher.stitch(opts, imgs, basename,
01165                             m);
01166             break;
01167         }
01168         case PanoramaOptions::TIFF_mask:
01169         case PanoramaOptions::TIFF_multilayer_mask:
01170             DEBUG_ERROR("multi mask stitching not implemented!");
01171             break;
01172         default:
01173             DEBUG_ERROR("output format " << opts.getFormatName(opts.outputFormat) << "not supported");
01174             break;
01175     }
01176 }
01177 
01184 IMPEX void stitchPanorama(const PanoramaData & pano,
01185                             const PanoramaOptions & opts,
01186                             AppBase::MultiProgressDisplay & progress,
01187                             const std::string & basename,
01188                     const UIntSet & usedImgs);
01189 
01190 // the instantiations of the stitching functions have been divided into two .cpp
01191 // files, because g++ will use too much memory otherwise (> 1.5 GB)
01192 
01193 void stitchPanoGray_8_16(const PanoramaData & pano,
01194                          const PanoramaOptions & opts,
01195                          AppBase::MultiProgressDisplay & progress,
01196                          const std::string & basename,
01197                          const UIntSet & usedImgs,
01198                          const char * pixelType);
01199 
01200 void stitchPanoGray_32_float(const PanoramaData & pano,
01201                              const PanoramaOptions & opts,
01202                              AppBase::MultiProgressDisplay & progress,
01203                              const std::string & basename,
01204                              const UIntSet & usedImgs,
01205                              const char * pixelType);
01206 
01207 
01208 void stitchPanoRGB_8_16(const PanoramaData & pano,
01209                         const PanoramaOptions & opts,
01210                         AppBase::MultiProgressDisplay & progress,
01211                         const std::string & basename,
01212                         const UIntSet & usedImgs,
01213                         const char * pixelType);
01214 
01215 void stitchPanoRGB_32_float(const PanoramaData & pano,
01216                             const PanoramaOptions & opts,
01217                             AppBase::MultiProgressDisplay & progress,
01218                             const std::string & basename,
01219                             const UIntSet & usedImgs,
01220                             const char * pixelType);
01221 
01222 } // namespace
01223 } // namespace
01224 
01225 #endif // _H

Generated on Mon Sep 20 01:01:26 2010 for Hugintrunk by doxygen 1.3.9.1