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