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

Generated on 3 Aug 2015 for Hugintrunk by  doxygen 1.4.7