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

Generated on 24 May 2016 for Hugintrunk by  doxygen 1.4.7