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 #include <vigra/copyimage.hxx>
00046 
00047 #include <vigra_ext/StitchWatershed.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 #include <nona/StitcherOptions.h>
00056 #include <algorithms/basic/LayerStacks.h>
00057 
00058 // somehow these are still
00059 #undef DIFFERENCE
00060 #undef min
00061 #undef max
00062 #undef MIN
00063 #undef MAX
00064 
00065 namespace HuginBase {
00066 namespace Nona {
00067 
00069 template <typename ImageType, typename AlphaType>
00070 class Stitcher
00071 {
00072 public:
00074     Stitcher(const PanoramaData & pano, AppBase::ProgressDisplay* progress)
00075         : m_pano(pano), m_progress(progress)
00076     {
00077     }
00078 
00079     virtual ~Stitcher() {};
00080 
00085     virtual void stitch(const PanoramaOptions & opts,
00086                         UIntSet & images, const std::string & file,
00087                         SingleImageRemapper<ImageType, AlphaType> & remapper)
00088     {
00089         m_images=images;
00090         calcOutputROIS(opts, images);
00091     };
00092 
00093 
00094     virtual UIntSet getUsedImages()
00095     {
00096         UIntSet ret;
00097         assert(m_rois.size() == m_images.size());
00098         std::vector<vigra::Rect2D>::iterator itr = m_rois.begin();
00099         for(UIntSet::iterator it = m_images.begin(); it != m_images.end(); ++it) {
00100             if (! itr->isEmpty()) {
00101                 ret.insert(*it);
00102             }
00103         }
00104         return ret;
00105     }
00106 
00107 protected:
00108     // calculate ROI's if not already known
00109     virtual void calcOutputROIS(const PanoramaOptions & opts, const UIntSet & images)
00110     {
00111         m_rois = HuginBase::ComputeImageROI::computeROIS(m_pano, opts, images);
00112     }
00113 
00114     const PanoramaData & m_pano;
00115     AppBase::ProgressDisplay* m_progress;
00116     UIntSet m_images;
00117     std::vector<vigra::Rect2D> m_rois;
00118 };
00119 
00120 namespace detail
00121 {
00122     template<typename ImageType, typename AlphaType>
00123     void saveRemapped(RemappedPanoImage<ImageType, AlphaType> & remapped,
00124         unsigned int imgNr, unsigned int nImg,
00125         const PanoramaOptions & opts,
00126         const std::string& basename,
00127         const bool useBigTIFF,
00128         AppBase::ProgressDisplay* progress)
00129     {
00130         ImageType * final_img = 0;
00131         AlphaType * alpha_img = 0;
00132         ImageType complete;
00133         vigra::BImage alpha;
00134 
00135         if (remapped.boundingBox().isEmpty())
00136             // do not save empty files...
00137             // TODO: need to tell other parts (enblend etc.) about it too!
00138             return;
00139 
00140         if (opts.outputMode == PanoramaOptions::OUTPUT_HDR)
00141         {
00142             // export alpha channel as gray channel (original pixel weights)
00143             std::ostringstream greyname;
00144             greyname << basename << std::setfill('0') << std::setw(4) << imgNr << "_gray.pgm";
00145             vigra::ImageExportInfo exinfo1(greyname.str().c_str());
00146             if (!opts.tiff_saveROI)
00147             {
00148                 alpha.resize(opts.getROI().size());
00149                 vigra::Rect2D newOutRect = remapped.boundingBox() & opts.getROI();
00150                 vigra::Rect2D newOutArea(newOutRect);
00151                 newOutRect.moveBy(-opts.getROI().upperLeft());
00152                 vigra::copyImage(vigra_ext::applyRect(newOutArea,
00153                     vigra_ext::srcMaskRange(remapped)),
00154                     vigra_ext::applyRect(newOutRect,
00155                     destImage(alpha)));
00156                 vigra::exportImage(srcImageRange(alpha), exinfo1);
00157             }
00158             else
00159             {
00160                 exinfo1.setPosition(remapped.boundingBox().upperLeft());
00161                 exinfo1.setCanvasSize(vigra::Size2D(opts.getWidth(), opts.getHeight()));
00162                 vigra::Rect2D rect = remapped.boundingBox();
00163                 if (rect.right() > opts.getROI().right())
00164                 {
00165                     rect &= opts.getROI();
00166                     rect.moveBy(-rect.upperLeft());
00167                     vigra::exportImage(srcImageRange(remapped.m_mask, rect), exinfo1);
00168                 }
00169                 else
00170                 {
00171                     vigra::exportImage(srcImageRange(remapped.m_mask), exinfo1);
00172                 };
00173             }
00174 
00175             // calculate real alpha for saving with the image
00176             progress->setMessage("Calculating mask");
00177             remapped.calcAlpha();
00178         }
00179 
00180         vigra::Rect2D subImage;
00181         if (!opts.tiff_saveROI)
00182         {
00183             // FIXME: this is stupid. Should not require space for full image...
00184             // but this would need a lower level interface in vigra impex
00185             complete.resize(opts.getROI().size());
00186             alpha.resize(opts.getROI().size());
00187             vigra::Rect2D newOutRect = remapped.boundingBox() & opts.getROI();
00188             vigra::Rect2D newOutArea(newOutRect);
00189             newOutRect.moveBy(-opts.getROI().upperLeft());
00190             vigra::copyImage(vigra_ext::applyRect(newOutArea,
00191                 vigra_ext::srcImageRange(remapped)),
00192                 vigra_ext::applyRect(newOutRect,
00193                 destImage(complete)));
00194 
00195             vigra::copyImage(vigra_ext::applyRect(newOutArea,
00196                 vigra_ext::srcMaskRange(remapped)),
00197                 vigra_ext::applyRect(newOutRect,
00198                 destImage(alpha)));
00199             final_img = &complete;
00200             alpha_img = &alpha;
00201         }
00202         else
00203         {
00204             final_img = &remapped.m_image;
00205             alpha_img = &remapped.m_mask;
00206             if (remapped.boundingBox().right() > opts.getROI().right())
00207             {
00208                 subImage = remapped.boundingBox() & opts.getROI();
00209                 subImage.moveBy(-subImage.upperLeft());
00210             };
00211         }
00212 
00213         std::string ext = opts.getOutputExtension();
00214         std::ostringstream filename;
00215         filename << basename << std::setfill('0') << std::setw(4) << imgNr << "." + ext;
00216 
00217         progress->setMessage("saving", hugin_utils::stripPath(filename.str()));
00218 
00219         vigra::ImageExportInfo exinfo(filename.str().c_str(), useBigTIFF ? "w8" : "w");
00220         exinfo.setXResolution(150);
00221         exinfo.setYResolution(150);
00222         exinfo.setICCProfile(remapped.m_ICCProfile);
00223         if (opts.tiff_saveROI)
00224         {
00225             exinfo.setPosition(remapped.boundingBox().upperLeft());
00226             exinfo.setCanvasSize(vigra::Size2D(opts.getWidth(), opts.getHeight()));
00227         }
00228         else
00229         {
00230             exinfo.setPosition(opts.getROI().upperLeft());
00231             exinfo.setCanvasSize(vigra::Size2D(opts.getWidth(), opts.getHeight()));
00232         }
00233         if (opts.outputPixelType.size() > 0)
00234         {
00235             exinfo.setPixelType(opts.outputPixelType.c_str());
00236         }
00237         bool supportsAlpha = true;
00238         if (ext == "tif")
00239         {
00240             exinfo.setCompression(opts.tiffCompression.c_str());
00241         }
00242         else
00243         {
00244             if (ext == "jpg")
00245             {
00246                 std::ostringstream quality;
00247                 quality << "JPEG QUALITY=" << opts.quality;
00248                 exinfo.setCompression(quality.str().c_str());
00249                 supportsAlpha = false;
00250             };
00251         }
00252 
00253         if (subImage.area() > 0)
00254         {
00255             if (supportsAlpha)
00256             {
00257                 vigra::exportImageAlpha(srcImageRange(*final_img, subImage), srcImage(*alpha_img), exinfo);
00258             }
00259             else
00260             {
00261                 vigra::exportImage(srcImageRange(*final_img, subImage), exinfo);
00262             };
00263         }
00264         else
00265         {
00266             if (supportsAlpha)
00267             {
00268                 vigra::exportImageAlpha(srcImageRange(*final_img), srcImage(*alpha_img), exinfo);
00269             }
00270             else
00271             {
00272                 vigra::exportImage(srcImageRange(*final_img), exinfo);
00273             };
00274         };
00275     };
00276 } // namespace detail
00277 
00279 template <typename ImageType, typename AlphaType>
00280 class MultiImageRemapper : public Stitcher<ImageType, AlphaType>
00281 {
00282 public:
00283 
00284     typedef Stitcher<ImageType, AlphaType> Base;
00285 
00286     MultiImageRemapper(const PanoramaData & pano,
00287                        AppBase::ProgressDisplay* progress)
00288     : Stitcher<ImageType,AlphaType>(pano, progress)
00289     {
00290     }
00291 
00292     virtual ~MultiImageRemapper()
00293     {
00294     }
00295 
00296     virtual void stitch(const PanoramaOptions & opts, UIntSet & images,
00297                         const std::string & basename,
00298                         SingleImageRemapper<ImageType, AlphaType> & remapper,
00299                         const AdvancedOptions& advOptions)
00300     {
00301         Base::stitch(opts, images, basename, remapper);
00302         DEBUG_ASSERT(opts.outputFormat == PanoramaOptions::TIFF_multilayer
00303                      || opts.outputFormat == PanoramaOptions::TIFF_m
00304                      || opts.outputFormat == PanoramaOptions::JPEG_m
00305                      || opts.outputFormat == PanoramaOptions::PNG_m
00306                      || opts.outputFormat == PanoramaOptions::HDR_m
00307                      || opts.outputFormat == PanoramaOptions::EXR_m);
00308 
00309         m_basename = basename;
00310 
00311         // setup the output.
00312         prepareOutputFile(opts, advOptions);
00313 
00314         // remap each image and save
00315         int i=0;
00316         for (UIntSet::const_iterator it = images.begin();
00317              it != images.end(); ++it)
00318         {
00319             // get a remapped image.
00320             PanoramaOptions modOptions(opts);
00321             if (GetAdvancedOption(advOptions, "ignoreExposure", false))
00322             {
00323                 modOptions.outputExposureValue = Base::m_pano.getImage(*it).getExposureValue();
00324             };
00325             RemappedPanoImage<ImageType, AlphaType> *
00326                 remapped = remapper.getRemapped(Base::m_pano, modOptions, *it, 
00327                                                 Base::m_rois[i], Base::m_progress);
00328             try {
00329                 saveRemapped(*remapped, *it, Base::m_pano.getNrOfImages(), opts, advOptions);
00330             } catch (vigra::PreconditionViolation & e) {
00331                 // this can be thrown, if an image
00332                 // is completely out of the pano
00333                 std::cerr << e.what();
00334             }
00335             // free remapped image
00336             remapper.release(remapped);
00337             i++;
00338         }
00339         finalizeOutputFile(opts);
00340         Base::m_progress->taskFinished();
00341     }
00342 
00344     virtual void prepareOutputFile(const PanoramaOptions & opts, const AdvancedOptions& advOptions)
00345     {
00346         Base::m_progress->setMessage("Multiple images output");
00347     }
00348 
00350     virtual void saveRemapped(RemappedPanoImage<ImageType, AlphaType> & remapped,
00351                               unsigned int imgNr, unsigned int nImg,
00352                               const PanoramaOptions & opts,
00353                               const AdvancedOptions& advOptions)
00354     {
00355         detail::saveRemapped(remapped, imgNr, nImg, opts, m_basename, GetAdvancedOption(advOptions, "useBigTIFF", false), Base::m_progress);
00356 
00357         if (opts.saveCoordImgs) {
00358             vigra::UInt16Image xImg;
00359             vigra::UInt16Image yImg;
00360 
00361             Base::m_progress->setMessage("creating coordinate images");
00362 
00363             remapped.calcSrcCoordImgs(xImg, yImg);
00364             vigra::UInt16Image dist;
00365             if (! opts.tiff_saveROI) {
00366                 dist.resize(opts.getWidth(), opts.getHeight());
00367                 vigra::copyImage(srcImageRange(xImg),
00368                                 vigra_ext::applyRect(remapped.boundingBox(),
00369                                                     destImage(dist)));
00370                 std::ostringstream filename2;
00371                 filename2 << m_basename << std::setfill('0') << std::setw(4) << imgNr << "_x.tif";
00372 
00373                 vigra::ImageExportInfo exinfo(filename2.str().c_str());
00374                 exinfo.setXResolution(150);
00375                 exinfo.setYResolution(150);
00376                 exinfo.setCompression(opts.tiffCompression.c_str());
00377                 vigra::exportImage(srcImageRange(dist), exinfo);
00378 
00379 
00380                 vigra::copyImage(srcImageRange(yImg),
00381                                 vigra_ext::applyRect(remapped.boundingBox(),
00382                                         destImage(dist)));
00383                 std::ostringstream filename3;
00384                 filename3 << m_basename << std::setfill('0') << std::setw(4) << imgNr << "_y.tif";
00385 
00386                 vigra::ImageExportInfo exinfo2(filename3.str().c_str());
00387                 exinfo2.setXResolution(150);
00388                 exinfo2.setYResolution(150);
00389                 exinfo2.setCompression(opts.tiffCompression.c_str());
00390                 vigra::exportImage(srcImageRange(dist), exinfo2);
00391             } else {
00392                 std::ostringstream filename2;
00393                 filename2 << m_basename << std::setfill('0') << std::setw(4) << imgNr << "_x.tif";
00394                 vigra::ImageExportInfo exinfo(filename2.str().c_str());
00395                 exinfo.setXResolution(150);
00396                 exinfo.setYResolution(150);
00397                 exinfo.setCompression(opts.tiffCompression.c_str());
00398                 vigra::exportImage(srcImageRange(xImg), exinfo);
00399                 std::ostringstream filename3;
00400                 filename3 << m_basename << std::setfill('0') << std::setw(4) << imgNr << "_y.tif";
00401                 vigra::ImageExportInfo exinfo2(filename3.str().c_str());
00402                 exinfo2.setXResolution(150);
00403                 exinfo2.setYResolution(150);
00404                 exinfo2.setCompression(opts.tiffCompression.c_str());
00405                 vigra::exportImage(srcImageRange(yImg), exinfo2);
00406             }
00407         }
00408     }
00409 
00410     virtual void finalizeOutputFile(const PanoramaOptions & opts)
00411     {
00412         Base::m_progress->taskFinished();
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::ProgressDisplay* progress)
00430         : MultiImageRemapper<ImageType, AlphaImageType> (pano, progress), m_tiff(NULL)
00431     {
00432     }
00433 
00434     virtual ~TiffMultiLayerRemapper()
00435     {
00436     }
00437 
00438     virtual void prepareOutputFile(const PanoramaOptions & opts, const AdvancedOptions& advOptions)
00439     {
00440         const std::string filename (Base::m_basename + ".tif");
00441         DEBUG_DEBUG("Layering image into a multi image tif file " << filename);
00442         Base::m_progress->setMessage("Multiple layer output");
00443         m_tiff = TIFFOpen(filename.c_str(), GetAdvancedOption(advOptions, "useBigTIFF", false) ? "w8" : "w");
00444         DEBUG_ASSERT(m_tiff && "could not open tiff output file");
00445     }
00446 
00448     virtual void saveRemapped(RemappedPanoImage<ImageType, AlphaImageType> & remapped,
00449                               unsigned int imgNr, unsigned int nImg,
00450                               const PanoramaOptions & opts,
00451                               const AdvancedOptions& advOptions)
00452     {
00453         if (remapped.boundingBox().isEmpty())
00454            return;
00455 
00456         DEBUG_DEBUG("Saving TIFF ROI");
00457         vigra_ext::createTiffDirectory(m_tiff,
00458                                        Base::m_pano.getImage(imgNr).getFilename(),
00459                                        Base::m_basename,
00460                                        opts.tiffCompression,
00461                                        imgNr+1, nImg, remapped.boundingBox().upperLeft(),
00462                                        opts.getROI().size(),
00463                                        remapped.m_ICCProfile);
00464         vigra_ext::createAlphaTiffImage(vigra::srcImageRange(remapped.m_image),
00465                                         vigra::maskImage(remapped.m_mask),
00466                                         m_tiff);
00467         TIFFFlush(m_tiff);
00468     }
00469 
00471     virtual void finalizeOutputFile(const PanoramaOptions & opts)
00472     {
00473             TIFFClose(m_tiff);
00474         Base::m_progress->setMessage("saved", hugin_utils::stripPath(Base::m_basename + ".tif"));
00475         Base::m_progress->taskFinished();
00476     }
00477 
00478 
00479 protected:
00480     vigra::TiffImage * m_tiff;
00481 };
00482 
00483 template <typename ImageType, typename AlphaType>
00484 class WeightedStitcher : public Stitcher<ImageType, AlphaType>
00485 {
00486 public:
00487 
00488     typedef Stitcher<ImageType, AlphaType> Base;
00489     WeightedStitcher(const PanoramaData & pano,
00490                      AppBase::ProgressDisplay* progress)
00491         : Stitcher<ImageType, AlphaType>(pano, progress)
00492     {
00493     }
00494 
00495     virtual ~WeightedStitcher() {};
00496 
00497     void stitch(const PanoramaOptions & opts, UIntSet & imgSet,
00498                         const std::string & filename,
00499                         ImageType& panoImage,
00500                         AlphaType& alpha,
00501                         SingleImageRemapper<ImageType, AlphaType> & remapper,
00502                         const AdvancedOptions& advOptions)
00503     {
00504         const unsigned int nImg = imgSet.size();
00505 
00506         Base::m_progress->setMessage("Remapping and stitching");
00507 
00508         const bool wrap = (opts.getHFOV() == 360.0) && (opts.getWidth()==opts.getROI().width());
00509         // remap each image and blend into main pano image
00510         const bool hardSeam = GetAdvancedOption(advOptions, "hardSeam", true);
00511         UIntVector images;
00512         if(hardSeam)
00513         { 
00514             std::copy(imgSet.begin(), imgSet.end(), std::back_inserter(images));
00515         }
00516         else
00517         {
00518             images = HuginBase::getEstimatedBlendingOrder(Base::m_pano, imgSet, opts.colorReferenceImage);
00519         };
00520         for (UIntVector::const_iterator it = images.begin(); it != images.end(); ++it)
00521         {
00522             // get a remapped image.
00523             DEBUG_DEBUG("remapping image: " << *it);
00524             PanoramaOptions modOptions(opts);
00525             if (GetAdvancedOption(advOptions, "ignoreExposure", false))
00526             {
00527                 modOptions.outputExposureValue = Base::m_pano.getImage(*it).getExposureValue();
00528             };
00529             RemappedPanoImage<ImageType, AlphaType> *
00530                 remapped = remapper.getRemapped(Base::m_pano, modOptions, *it,
00531                     Base::m_rois[std::distance(imgSet.begin(), imgSet.find(*it))], Base::m_progress);
00532             if(iccProfile.size()==0)
00533             {
00534                 iccProfile=remapped->m_ICCProfile;
00535             };
00536             if (GetAdvancedOption(advOptions, "saveIntermediateImages", false))
00537             {
00538                 modOptions.outputFormat = PanoramaOptions::TIFF_m;
00539                 modOptions.tiff_saveROI = true;
00540                 std::string finalFilename(GetAdvancedOption(advOptions, "basename", filename));
00541                 const std::string suffix(GetAdvancedOption(advOptions, "saveIntermediateImagesSuffix"));
00542                 if (!suffix.empty())
00543                 {
00544                     finalFilename.append(suffix);
00545                 };
00546                 detail::saveRemapped(*remapped, *it, nImg, modOptions, finalFilename, GetAdvancedOption(advOptions, "useBigTIFF", false), Base::m_progress);
00547             }
00548             Base::m_progress->setMessage("blending", hugin_utils::stripPath(Base::m_pano.getImage(*it).getFilename()));
00549             // add image to pano and panoalpha, adjusts panoROI as well.
00550             try {
00551                 vigra_ext::MergeImages<ImageType, AlphaType>(panoImage, alpha, remapped->m_image, remapped->m_mask, vigra::Diff2D(remapped->boundingBox().upperLeft()), wrap, hardSeam);
00552                 // update bounding box of the panorama
00553                 m_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         }
00562         // check if our intermediate image covers whole canvas
00563         // if not update m_panoROI
00564         if (m_panoROI.width() < opts.getROI().width() || m_panoROI.height() < opts.getROI().height())
00565         {
00566             // update m_panoROI
00567             m_panoROI = opts.getROI();
00568         }
00569     }
00570 
00571     void stitch(const PanoramaOptions & opts, UIntSet & imgSet,
00572                         const std::string & filename,
00573                         SingleImageRemapper<ImageType, AlphaType> & remapper,
00574                         const AdvancedOptions& advOptions)
00575     {
00576         Base::stitch(opts, imgSet, filename, remapper);
00577 
00578         std::string basename = filename;
00579 
00580         // create panorama canvas
00581         ImageType pano(opts.getWidth(), opts.getHeight());
00582         AlphaType panoMask(opts.getWidth(), opts.getHeight());
00583 
00584         stitch(opts, imgSet, filename, pano, panoMask, remapper, advOptions);
00585         
00586             std::string ext = opts.getOutputExtension();
00587         std::string cext = hugin_utils::tolower(hugin_utils::getExtension(basename));
00588         std::transform(cext.begin(),cext.end(), cext.begin(), (int(*)(int))std::tolower);
00589         // remove extension only if it specifies the same file type, otherwise
00590         // its probably part of the filename.
00591         if (cext == ext) {
00592             basename = hugin_utils::stripExtension(basename);
00593         }
00594         std::string outputfile = basename + "." + ext;
00595         
00596         // save the remapped image
00597         Base::m_progress->setMessage("saving result", hugin_utils::stripPath(outputfile));
00598         DEBUG_DEBUG("Saving panorama: " << outputfile);
00599         vigra::ImageExportInfo exinfo(outputfile.c_str(), GetAdvancedOption(advOptions, "useBigTIFF", false) ? "w8" : "w");
00600         exinfo.setXResolution(150);
00601         exinfo.setYResolution(150);
00602         exinfo.setICCProfile(iccProfile);
00603         if (opts.outputPixelType.size() > 0) {
00604             exinfo.setPixelType(opts.outputPixelType.c_str());
00605         }
00606 
00607         // set compression quality for jpeg images.
00608         if (opts.outputFormat == PanoramaOptions::JPEG) {
00609             std::ostringstream quality;
00610             quality << "JPEG QUALITY=" << opts.quality;
00611             exinfo.setCompression(quality.str().c_str());
00612             // scale down to UInt8 if necessary
00613             vigra_ext::ConvertTo8Bit(pano);
00614             // force 8 bit depth for jpeg output
00615             exinfo.setPixelType("UINT8");
00616             vigra::exportImage(srcImageRange(pano, m_panoROI), exinfo);
00617         } else if (opts.outputFormat == PanoramaOptions::TIFF) {
00618             exinfo.setCompression(opts.tiffCompression.c_str());
00619             exinfo.setCanvasSize(pano.size());
00620             exinfo.setPosition(m_panoROI.upperLeft());
00621             vigra::exportImageAlpha(srcImageRange(pano, m_panoROI),
00622                                            srcImage(panoMask, m_panoROI.upperLeft()), exinfo);
00623         } else if (opts.outputFormat == PanoramaOptions::HDR) {
00624             exinfo.setPixelType("FLOAT");
00625             vigra::exportImage(srcImageRange(pano), exinfo);
00626         } else {
00627             vigra::exportImageAlpha(srcImageRange(pano, m_panoROI),
00628                                            srcImage(panoMask, m_panoROI.upperLeft()), exinfo);
00629         }
00630         /*
00631 #ifdef DEBUG
00632         vigra::exportImage(srcImageRange(panoMask), vigra::ImageExportInfo("pano_alpha.tif"));
00633 #endif
00634         */
00635     }
00636 
00637 protected:
00638     vigra::ImageImportInfo::ICCProfile iccProfile;
00639     vigra::Rect2D m_panoROI;
00640 };
00641 
00642 
00644 template<class VALUETYPE>
00645 struct ReduceToDifferenceFunctor
00646 {
00647     typedef VALUETYPE  argument_type;
00648     typedef VALUETYPE  result_type;
00649     typedef typename vigra::NumericTraits<argument_type> Traits;
00650     typedef typename Traits::RealPromote float_type;
00651 
00652 
00653     ReduceToDifferenceFunctor()
00654     {
00655         reset();
00656     }
00657 
00658     void reset()
00659     {
00660         sum = vigra::NumericTraits<float_type>::zero();
00661         values.clear();
00662     }
00663 
00664     template<class T, class M> 
00665     void operator() (T const &v, M const &a)
00666     {
00667         if (a) {
00668             values.push_back(v);
00669             sum = sum + v;
00670         }
00671     }
00672 
00674     float_type operator()() const
00675     {
00676         typedef typename std::vector<float_type>::const_iterator Iter;
00677         if (values.size() > 1) {
00678             float_type mean = sum / values.size();
00679             float_type error = vigra::NumericTraits<float_type>::zero();
00680             for (Iter it= values.begin();  it != values.end(); ++it) {
00681                 error +=  vigra::abs(*it-mean);
00682             }
00683             return error;
00684         } else {
00685             return sum;
00686         }
00687     }
00688     std::vector<float_type> values;
00689     float_type sum;
00690 };
00691 
00696 template <typename ImageType, typename AlphaType>
00697 class ReduceStitcher : public Stitcher<ImageType, AlphaType>
00698 {
00699     typedef Stitcher<ImageType, AlphaType> Base;
00700 public:
00701     ReduceStitcher(const PanoramaData & pano,
00702                    AppBase::ProgressDisplay* progress)
00703     : Stitcher<ImageType, AlphaType>(pano, progress)
00704     {
00705     }
00706 
00707     virtual ~ReduceStitcher()
00708     {
00709     }
00710 
00711     template <class FUNCTOR>
00712     void stitch(const PanoramaOptions & opts, UIntSet & imgSet,
00713                 const std::string & filename,
00714                 SingleImageRemapper<ImageType, AlphaType> & remapper,
00715                 FUNCTOR & reduce,
00716                 const AdvancedOptions& advOptions)
00717     {
00718         Base::stitch(opts, imgSet, filename, remapper);
00719 
00720         std::string basename = filename;
00721 
00722     // create panorama canvas
00723         ImageType pano(opts.getWidth(), opts.getHeight());
00724         AlphaType panoMask(opts.getWidth(), opts.getHeight());
00725 
00726         stitch(opts, imgSet, vigra::destImageRange(pano), vigra::destImage(panoMask),
00727                remapper, reduce);
00728 
00729         std::string ext = opts.getOutputExtension();
00730         std::string cext = hugin_utils::tolower(hugin_utils::getExtension(basename));
00731         // remove extension only if it specifies the same file type, otherwise
00732         // its probably part of the filename.
00733         if (cext == ext) {
00734             basename = hugin_utils::stripExtension(basename);
00735         }
00736         std::string outputfile = basename + "." + ext;
00737 
00738 //        Base::m_progress.setMessage("saving result: " + hugin_utils::stripPath(outputfile));
00739         DEBUG_DEBUG("Saving panorama: " << outputfile);
00740         vigra::ImageExportInfo exinfo(outputfile.c_str(), GetAdvancedOption(advOptions, "useBigTIFF", false) ? "w8" : "w");
00741         if (opts.outputPixelType.size() > 0) {
00742             exinfo.setPixelType(opts.outputPixelType.c_str());
00743         }
00744         exinfo.setXResolution(150);
00745         exinfo.setYResolution(150);
00746         exinfo.setICCProfile(iccProfile);
00747         // set compression quality for jpeg images.
00748         if (opts.outputFormat == PanoramaOptions::JPEG) {
00749             std::ostringstream quality;
00750             quality << "JPEG QUALITY=" << opts.quality;
00751             exinfo.setCompression(quality.str().c_str());
00752             vigra::exportImage(srcImageRange(pano), exinfo);
00753         } else if (opts.outputFormat == PanoramaOptions::TIFF) {
00754             exinfo.setCompression(opts.tiffCompression.c_str());
00755             vigra::exportImageAlpha(srcImageRange(pano),
00756                                     srcImage(panoMask), exinfo);
00757         } else if (opts.outputFormat == PanoramaOptions::HDR) {
00758             vigra::exportImage(srcImageRange(pano), exinfo);
00759         } else {
00760             vigra::exportImageAlpha(srcImageRange(pano),
00761                                     srcImage(panoMask), exinfo);
00762         }
00763         /*
00764 #ifdef DEBUG
00765         vigra::exportImage(srcImageRange(panoMask), vigra::ImageExportInfo("pano_alpha.tif"));
00766 #endif
00767         */
00768     }
00769 
00770     
00771     template<class ImgIter, class ImgAccessor,
00772              class AlphaIter, class AlphaAccessor,
00773              class FUNCTOR>
00774     void stitch(const PanoramaOptions & opts, UIntSet & imgSet,
00775                 vigra::triple<ImgIter, ImgIter, ImgAccessor> pano,
00776                 std::pair<AlphaIter, AlphaAccessor> alpha,
00777                 SingleImageRemapper<ImageType, AlphaType> & remapper,
00778                 FUNCTOR & reduce)
00779     {
00780         typedef typename vigra::NumericTraits<typename ImageType::value_type> Traits;
00781         typedef typename AlphaAccessor::value_type MaskType;
00782 
00783         Base::stitch(opts, imgSet, "dummy", remapper);
00784 
00785         // remap all images..
00786         typedef std::vector<RemappedPanoImage<ImageType, AlphaType> *> RemappedVector;
00787         unsigned int nImg = imgSet.size();
00788 
00789         Base::m_progress->setMessage("Stitching");
00790         // empty ROI
00791         //      vigra::Rect2D panoROI;
00792         // remap all images..
00793         RemappedVector remapped(nImg);
00794 
00795         int i=0;
00796         // remap each image and blend into main pano image
00797         for (UIntSet::const_iterator it = imgSet.begin();
00798                 it != imgSet.end(); ++it)
00799         {
00800             // get a copy of the remapped image.
00801             // not very good, keeps all images in memory,
00802             // but should be enought for the preview.
00803             remapped[i] = remapper.getRemapped(Base::m_pano, opts, *it,
00804                                                Base::m_rois[i], Base::m_progress);
00805             if(iccProfile.size()==0)
00806             {
00807                 iccProfile=remapped[i]->m_ICCProfile;
00808             };
00809             i++;
00810         }
00811         vigra::Diff2D size =  pano.second - pano.first;
00812         ImgIter output = pano.first;
00813         // iterate over the whole image...
00814         // calculate something on the pixels that belong together..
00815         for (int y=0; y < size.y; y++) {
00816             for (int x=0; x < size.x; x++) {
00817                 reduce.reset();
00818                 MaskType maskRes=0;
00819                 for (unsigned int i=0; i< nImg; i++) {
00820                     MaskType a = remapped[i]->getMask(x,y);
00821                     if (a) {
00822                         maskRes = vigra_ext::LUTTraits<MaskType>::max();
00823                         reduce(remapped[i]->operator()(x,y), a);
00824                     }
00825                 }
00826                 pano.third.set(Traits::fromRealPromote(reduce()), output, vigra::Diff2D(x,y));
00827                 alpha.second.set(maskRes, alpha.first, vigra::Diff2D(x,y));
00828             }
00829         }
00830 
00831         for (typename RemappedVector::iterator it=remapped.begin();
00832              it != remapped.end(); ++it)
00833         {
00834             remapper.release(*it);
00835         }
00836     }
00837 
00838 public:
00839     vigra::ImageImportInfo::ICCProfile iccProfile;
00840 };
00841 
00844 template <typename ImageType, typename AlphaType>
00845 class SimpleStitcher : public Stitcher<ImageType, AlphaType>
00846 {
00847     typedef Stitcher<ImageType, AlphaType> Base;
00848 public:
00849     SimpleStitcher(const PanoramaData & pano,
00850                      AppBase::ProgressDisplay* progress)
00851         : Stitcher<ImageType, AlphaType>(pano, progress)
00852     {
00853     }
00854 
00855     virtual ~SimpleStitcher()
00856     {
00857     }
00858 
00859     template<class ImgIter, class ImgAccessor,
00860              class AlphaIter, class AlphaAccessor,
00861              class BlendFunctor>
00862     void stitch(const PanoramaOptions & opts, UIntSet & imgSet,
00863                 vigra::triple<ImgIter, ImgIter, ImgAccessor> pano,
00864                 std::pair<AlphaIter, AlphaAccessor> alpha,
00865                 SingleImageRemapper<ImageType, AlphaType> & remapper,
00866                 BlendFunctor & blend)
00867     {
00868         Base::m_images=imgSet;
00869         Base::calcOutputROIS(opts, imgSet);
00870 
00871         Base::m_progress->setMessage("Remapping and stitching with watershed algorithm");
00872         // empty ROI
00873         vigra::Rect2D panoROI;
00874 
00875             unsigned i=0;
00876         // remap each image and blend into main pano image
00877         for (UIntSet::reverse_iterator it = imgSet.rbegin();
00878             it != imgSet.rend(); ++it)
00879         {
00880             // get a remapped image.
00881             RemappedPanoImage<ImageType, AlphaType> *
00882             remapped = remapper.getRemapped(Base::m_pano, opts, *it,
00883                                             Base::m_rois[i], Base::m_progress);
00884             if (iccProfile.size() == 0) {
00885                 // try to extract icc profile.
00886                 iccProfile = remapped->m_ICCProfile;
00887             }
00888             Base::m_progress->setMessage("blending");
00889             // add image to pano and panoalpha, adjusts panoROI as well.
00890             try {
00891                 blend(*remapped, pano, alpha, panoROI);
00892                 // update bounding box of the panorama
00893                 panoROI = panoROI | remapped->boundingBox();
00894             } catch (vigra::PreconditionViolation & e) {
00895                 // this can be thrown, if an image
00896                 // is completely out of the pano
00897                                 std::cerr << e.what();
00898             }
00899             // free remapped image
00900             remapper.release(remapped);
00901             i++;
00902         }
00903         Base::m_progress->taskFinished();
00904     }
00905 
00906     template <class BlendFunctor>
00907     void stitch(const PanoramaOptions & opts, UIntSet & imgSet,
00908                 const std::string & filename,
00909                 SingleImageRemapper<ImageType, AlphaType> & remapper,
00910                 BlendFunctor & blend)
00911     {
00912         std::string basename = filename;
00913 
00914         // create panorama canvas
00915         ImageType pano(opts.getWidth(), opts.getHeight());
00916         AlphaType panoMask(opts.getWidth(), opts.getHeight());
00917 
00918         stitch(opts, imgSet, vigra::destImageRange(pano), vigra::destImage(panoMask), remapper, blend);
00919         
00920             std::string ext = opts.getOutputExtension();
00921         std::string cext = hugin_utils::tolower(hugin_utils::getExtension(basename));
00922         // remove extension only if it specifies the same file type, otherwise
00923         // its probably part of the filename.
00924         if (cext == ext) {
00925             basename = hugin_utils::stripExtension(basename);
00926         }
00927         std::string outputfile = basename + "." + ext;
00928         
00929         Base::m_progress.setMessage("saving result:", hugin_utils::stripPath(outputfile));
00930         DEBUG_DEBUG("Saving panorama: " << outputfile);
00931         vigra::ImageExportInfo exinfo(outputfile.c_str());
00932         exinfo.setXResolution(150);
00933         exinfo.setYResolution(150);
00934         exinfo.setICCProfile(iccProfile);
00935         // set compression quality for jpeg images.
00936         if (opts.outputFormat == PanoramaOptions::JPEG) {
00937             std::ostringstream quality;
00938             quality << "JPEG QUALITY=" << opts.quality;
00939             exinfo.setCompression(quality.str().c_str());
00940             vigra::exportImage(srcImageRange(pano), exinfo);
00941         } else if (opts.outputFormat == PanoramaOptions::TIFF) {
00942             exinfo.setCompression("DEFLATE");
00943             vigra::exportImageAlpha(srcImageRange(pano),
00944                                            srcImage(panoMask), exinfo);
00945         } else {
00946             vigra::exportImageAlpha(srcImageRange(pano),
00947                                            srcImage(panoMask), exinfo);
00948         }
00949         /*
00950 #ifdef DEBUG
00951         vigra::exportImage(srcImageRange(panoMask), vigra::ImageExportInfo("pano_alpha.tif"));
00952 #endif
00953         */
00954         Base::m_progress.popTask();
00955 
00956     }
00957 public:
00958     vigra::ImageExportInfo::ICCProfile iccProfile;
00959 };
00960 
00964 struct StackingBlender
00965 {
00966 
00967 public:
00972     template <typename ImageType, typename AlphaType,
00973               typename PanoIter, typename PanoAccessor,
00974               typename AlphaIter, typename AlphaAccessor>
00975     void operator()(RemappedPanoImage<ImageType, AlphaType> & img,
00976                     vigra::triple<PanoIter, PanoIter, PanoAccessor> pano,
00977                     std::pair<AlphaIter, AlphaAccessor> alpha,
00978                     const vigra::Rect2D & panoROI)
00979     {
00980         DEBUG_DEBUG("pano roi: " << panoROI << " img roi: " << img.boundingBox());
00981 
00982 //        DEBUG_DEBUG("no overlap, copying upper area. imgroi " << img.roi());
00983 //        DEBUG_DEBUG("pano roi: " << panoROI.upperLeft() << " -> " << panoROI.lowerRight());
00984         DEBUG_DEBUG("size of panorama: " << pano.second - pano.first);
00985 
00986                 // check if bounding box of image is outside of panorama...
00987                 vigra::Rect2D fullPano(vigra::Size2D(pano.second-pano.first));
00988         // blend only the intersection (which is inside the pano..)
00989         vigra::Rect2D overlap = fullPano & img.boundingBox();
00990 
00991         vigra::copyImageIf(vigra_ext::applyRect(overlap, vigra_ext::srcImageRange(img)),
00992                            vigra_ext::applyRect(overlap, vigra_ext::srcMask(img)),
00993                            vigra_ext::applyRect(overlap, std::make_pair(pano.first, pano.third)));
00994         // copy mask
00995         vigra::copyImageIf(vigra_ext::applyRect(overlap, srcMaskRange(img)),
00996                            vigra_ext::applyRect(overlap, srcMask(img)),
00997                            vigra_ext::applyRect(overlap, alpha));
00998     }
00999 };
01000 
01001 template<typename ImageType, typename AlphaType>
01002 static void stitchPanoIntern(const PanoramaData & pano,
01003                              const PanoramaOptions & opts,
01004                              AppBase::ProgressDisplay* progress,
01005                              const std::string & basename,
01006                              UIntSet imgs,
01007                              const AdvancedOptions& advOptions)
01008 {
01009     FileRemapper<ImageType, AlphaType> m;
01010     // determine stitching output
01011     switch (opts.outputFormat) {
01012         case PanoramaOptions::JPEG:
01013         case PanoramaOptions::PNG:
01014         case PanoramaOptions::TIFF:
01015         case PanoramaOptions::HDR:
01016         case PanoramaOptions::EXR:
01017         {
01018             if (opts.outputMode == PanoramaOptions::OUTPUT_HDR) {
01019                 vigra_ext::ReduceToHDRFunctor<typename ImageType::value_type> hdrmerge;
01020                 ReduceStitcher<ImageType, AlphaType> stitcher(pano, progress);
01021                 stitcher.stitch(opts, imgs, basename, m, hdrmerge, advOptions);
01022             } else {
01023                 WeightedStitcher<ImageType, AlphaType> stitcher(pano, progress);
01024                 m.setAdvancedOptions(advOptions);
01025                 stitcher.stitch(opts, imgs, basename, m, advOptions);
01026             }
01027             break;
01028         }
01029         case PanoramaOptions::JPEG_m:
01030         case PanoramaOptions::PNG_m:
01031         case PanoramaOptions::TIFF_m:
01032         case PanoramaOptions::HDR_m:
01033         case PanoramaOptions::EXR_m:
01034         {
01035             MultiImageRemapper<ImageType, AlphaType> stitcher(pano, progress);
01036             m.setAdvancedOptions(advOptions);
01037             stitcher.stitch(opts, imgs, basename, m, advOptions);
01038             break;
01039         }
01040         case PanoramaOptions::TIFF_multilayer:
01041         {
01042             TiffMultiLayerRemapper<ImageType, AlphaType> stitcher(pano, progress);
01043             stitcher.stitch(opts, imgs, basename, m, advOptions);
01044             break;
01045         }
01046         case PanoramaOptions::TIFF_mask:
01047         case PanoramaOptions::TIFF_multilayer_mask:
01048             DEBUG_ERROR("multi mask stitching not implemented!");
01049             break;
01050         default:
01051             DEBUG_ERROR("output format " << opts.getFormatName(opts.outputFormat) << "not supported");
01052             break;
01053     }
01054 }
01055 
01062 IMPEX void stitchPanorama(const PanoramaData & pano,
01063                     const PanoramaOptions & opts,
01064                     AppBase::ProgressDisplay* progress,
01065                     const std::string & basename,
01066                     const UIntSet & usedImgs,
01067                     const AdvancedOptions& advOptions = AdvancedOptions());
01068 
01069 // the instantiations of the stitching functions have been divided into two .cpp
01070 // files, because g++ will use too much memory otherwise (> 1.5 GB)
01071 
01072 void stitchPanoGray_8_16(const PanoramaData & pano,
01073                          const PanoramaOptions & opts,
01074                          AppBase::ProgressDisplay* progress,
01075                          const std::string & basename,
01076                          const UIntSet & usedImgs,
01077                          const char * pixelType,
01078                          const AdvancedOptions& advOptions);
01079 
01080 void stitchPanoGray_32_float(const PanoramaData & pano,
01081                              const PanoramaOptions & opts,
01082                              AppBase::ProgressDisplay* progress,
01083                              const std::string & basename,
01084                              const UIntSet & usedImgs,
01085                              const char * pixelType,
01086                              const AdvancedOptions& advOptions);
01087 
01088 
01089 void stitchPanoRGB_8_16(const PanoramaData & pano,
01090                         const PanoramaOptions & opts,
01091                         AppBase::ProgressDisplay* progress,
01092                         const std::string & basename,
01093                         const UIntSet & usedImgs,
01094                         const char * pixelType,
01095                         const AdvancedOptions& advOptions);
01096 
01097 void stitchPanoRGB_32_float(const PanoramaData & pano,
01098                             const PanoramaOptions & opts,
01099                             AppBase::ProgressDisplay* progress,
01100                             const std::string & basename,
01101                             const UIntSet & usedImgs,
01102                             const char * pixelType,
01103                             const AdvancedOptions& advOptions);
01104 
01105 } // namespace
01106 } // namespace
01107 
01108 #endif // _H

Generated on 16 Dec 2017 for Hugintrunk by  doxygen 1.4.7