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() & 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             // calculate real alpha for saving with the image
00301             Base::m_progress.setMessage("Calculating mask");
00302             remapped.calcAlpha();
00303         }
00304 
00305         vigra::Rect2D subImage;
00306         if (! opts.tiff_saveROI) {
00307             // FIXME: this is stupid. Should not require space for full image...
00308             // but this would need a lower level interface in vigra impex
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         std::string cext = hugin_utils::getExtension(m_basename);
00336         std::transform(cext.begin(),cext.end(), cext.begin(), (int(*)(int))std::tolower);
00337         // remove extension only if it specifies the same file type, otherwise
00338         // its probably part of the filename.
00339         if (cext == ext) {
00340             m_basename = hugin_utils::stripExtension(m_basename);
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         // calculate stitching order
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         // empty ROI
00552         vigra::Rect2D panoROI;
00553 
00554         int i=0;
00555         // remap each image and blend into main pano image
00556         for (UIntVector::const_iterator it = images.begin();
00557              it != images.end(); ++it)
00558         {
00559             // get a remapped image.
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             // add image to pano and panoalpha, adjusts panoROI as well.
00570             try {
00571                 vigra_ext::blend(*remapped, pano, alpha, panoROI,
00572                                  Base::m_progress);
00573                 // update bounding box of the panorama
00574                 panoROI = panoROI | remapped->boundingBox();
00575             } catch (vigra::PreconditionViolation & e) {
00576                 DEBUG_ERROR("exception during stitching" << e.what());
00577                 // this can be thrown, if an image
00578                 // is completely out of the pano
00579             }
00580             // free remapped image
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         // create panorama canvas
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         // remove extension only if it specifies the same file type, otherwise
00604         // its probably part of the filename.
00605         if (cext == ext) {
00606             basename = hugin_utils::stripExtension(basename);
00607         }
00608         std::string outputfile = basename + "." + ext;
00609         
00610         // save the remapped image
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         // set compression quality for jpeg images.
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 #ifdef DEBUG
00640         vigra::exportImage(srcImageRange(panoMask), vigra::ImageExportInfo("pano_alpha.tif"));
00641 #endif
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     // create panorama canvas
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         // remove extension only if it specifies the same file type, otherwise
00741         // its probably part of the filename.
00742         if (cext == ext) {
00743             basename = hugin_utils::stripExtension(basename);
00744         }
00745         std::string outputfile = basename + "." + ext;
00746 
00747 //        Base::m_progress.setMessage("saving result: " + hugin_utils::stripPath(outputfile));
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         // set compression quality for jpeg images.
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 #ifdef DEBUG
00774         vigra::exportImage(srcImageRange(panoMask), vigra::ImageExportInfo("pano_alpha.tif"));
00775 #endif
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         // remap all images..
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         // empty ROI
00804         //      vigra::Rect2D panoROI;
00805         // remap all images..
00806         RemappedVector remapped(nImg);
00807 
00808         int i=0;
00809         // remap each image and blend into main pano image
00810         for (UIntSet::const_iterator it = imgSet.begin();
00811                 it != imgSet.end(); ++it)
00812         {
00813             // get a copy of the remapped image.
00814             // not very good, keeps all images in memory,
00815             // but should be enought for the preview.
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         // iterate over the whole image...
00827         // calculate something on the pixels that belong together..
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         // empty ROI
00889         vigra::Rect2D panoROI;
00890 
00891             unsigned i=0;
00892         // remap each image and blend into main pano image
00893         for (UIntSet::reverse_iterator it = imgSet.rbegin();
00894             it != imgSet.rend(); ++it)
00895         {
00896             // get a remapped image.
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                 // try to extract icc profile.
00902                 iccProfile = remapped->m_ICCProfile;
00903             }
00904             Base::m_progress.setMessage("blending");
00905             // add image to pano and panoalpha, adjusts panoROI as well.
00906             try {
00907                 blend(*remapped, pano, alpha, panoROI);
00908                 // update bounding box of the panorama
00909                 panoROI = panoROI | remapped->boundingBox();
00910             } catch (vigra::PreconditionViolation & e) {
00911                 // this can be thrown, if an image
00912                 // is completely out of the pano
00913                                 std::cerr << e.what();
00914             }
00915             // free remapped image
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         // create panorama canvas
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         // remove extension only if it specifies the same file type, otherwise
00940         // its probably part of the filename.
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         // set compression quality for jpeg images.
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 #ifdef DEBUG
00968         vigra::exportImage(srcImageRange(panoMask), vigra::ImageExportInfo("pano_alpha.tif"));
00969 #endif
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 //        DEBUG_DEBUG("no overlap, copying upper area. imgroi " << img.roi());
01003 //        DEBUG_DEBUG("pano roi: " << panoROI.upperLeft() << " -> " << panoROI.lowerRight());
01004         DEBUG_DEBUG("size of panorama: " << pano.second - pano.first);
01005 
01006                 // check if bounding box of image is outside of panorama...
01007                 vigra::Rect2D fullPano(vigra::Size2D(pano.second-pano.first));
01008         // blend only the intersection (which is inside the pano..)
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         // copy mask
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                 // check if bounding box of image is outside of panorama...
01065                 vigra::Rect2D fullPano(vigra::Size2D(pano.second-pano.first));
01066         // blend only the intersection (which is inside the pano..)
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         // copy mask
01076         vigra::copyImageIf(applyRect(overlap, srcMaskRange(img)),
01077                            applyRect(overlap, srcMask(img)),
01078                            applyRect(overlap, alpha));
01079     }
01080 };
01081 
01082 /*
01083 template <typename ImageType, typename AlphaImageType>
01084 class MultiResStitcher : public WeightedSticher<ImageType, AlphaImageType>
01085 {
01086 public:
01087     MultiResStitcher(const PanoramaData & pano,
01088                      AppBase::MultiProgressDisplay & progress)
01089         : WeightedStitcher(pano, progress)
01090     {
01091     }
01092 
01093     template <typename PanoIter, typename PanoAccessor,
01094               typename MaskIter, typename MaskAccessor>
01095     virtual void blendOverlap(vigra::triple<PanoIter, PanoIter, PanoAccessor> image,
01096                       std::pair<MaskIter, MaskAccessor> imageMask,
01097                       std::pair<PanoIter, PanoAccessor> pano,
01098                       std::pair<MaskIter, MaskAccessor> panoMask)
01099     {
01100         vigra::Diff2D size = image.second - image.first;
01101 
01102         // create new blending masks
01103         vigra::BasicImage<typename MaskIter::value_type> blendPanoMask(size);
01104         vigra::BasicImage<typename MaskIter::value_type> blendImageMask(size);
01105 
01106         // calculate the stitching masks.
01107         vigra_ext::nearestFeatureTransform(srcIterRange(panoMask.first, panoMask.first + size),
01108                                            imageMask,
01109                                            destImage(blendPanoMask),
01110                                            destImage(blendImageMask),
01111                                            m_progress);
01112 
01113         // calculate the a number of
01114 
01115         // copy the image into the panorama
01116         vigra::copyImageIf(image, maskImage(blendImageMask), pano);
01117         // copy mask
01118         vigra::copyImageIf(srcImageRange(blendImageMask), maskImage(blendImageMask), panoMask);
01119     }
01120 
01121 private:
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     // get all remapped images
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     //    typedef
01158     //        vigra::NumericTraits<typename OutputImageType::Accessor::value_type> DestTraits;
01159 
01160     FileRemapper<ImageType, AlphaType> m;
01161     // determine stitching output
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 // the instantiations of the stitching functions have been divided into two .cpp
01219 // files, because g++ will use too much memory otherwise (> 1.5 GB)
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 } // namespace
01251 } // namespace
01252 
01253 #endif // _H

Generated on Wed Sep 17 01:25:37 2014 for Hugintrunk by  doxygen 1.3.9.1