Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
RemappedPanoImage.h
Go to the documentation of this file.
1 // -*- c-basic-offset: 4 -*-
27 #ifndef _NONA_REMAPPEDPANOIMAGE_H
28 #define _NONA_REMAPPEDPANOIMAGE_H
29 
30 #include <vigra/imageinfo.hxx>
31 #include <vigra/initimage.hxx>
32 #include <vigra/copyimage.hxx>
33 #include <vigra/flatmorphology.hxx>
34 #include <vigra_ext/ROIImage.h>
35 #include <vigra_ext/openmp_vigra.h>
36 
38 #include <nona/StitcherOptions.h>
39 
40 #include <panodata/SrcPanoImage.h>
41 #include <panodata/Mask.h>
44 
45 // default values for exposure cutoff
46 #define NONA_DEFAULT_EXPOSURE_LOWER_CUTOFF 1/255.0f
47 #define NONA_DEFAULT_EXPOSURE_UPPER_CUTOFF 250/255.0f
48 
49 
50 namespace HuginBase {
51 namespace Nona {
52 
53 
60 template <class TRANSFORM>
61 void estimateImageRect(const SrcPanoImage & src,
62  const PanoramaOptions & dest,
63  TRANSFORM & transf,
64  vigra::Rect2D & imgRect);
65 
67 template <class TRANSFORM>
68 void estimateImageAlpha(const SrcPanoImage & src,
69  const PanoramaOptions & dest,
70  TRANSFORM & transf,
71  vigra::Rect2D & imgRect,
72  vigra::BImage & alpha,
73  double & scale);
74 
75 
79 template <class RemapImage, class AlphaImage>
80 class RemappedPanoImage : public vigra_ext::ROIImage<RemapImage, AlphaImage>
81 {
82 
84 
85  public:
86  // typedefs for the children types
87  typedef typename RemapImage::value_type image_value_type;
88  typedef typename RemapImage::traverser image_traverser;
89  typedef typename RemapImage::const_traverser const_image_traverser;
90  typedef typename RemapImage::Accessor ImageAccessor;
91  typedef typename RemapImage::ConstAccessor ConstImageAccessor;
92 
93  typedef typename AlphaImage::value_type mask_value_type;
94  typedef typename AlphaImage::traverser mask_traverser;
95  typedef typename AlphaImage::const_traverser const_mask_traverser;
96  typedef typename AlphaImage::Accessor MaskAccessor;
97  typedef typename AlphaImage::ConstAccessor ConstMaskAccessor;
98 
100 
101 
102  public:
108  {};
109 
110 
111  public:
113  void setPanoImage(const SrcPanoImage & src,
114  const PanoramaOptions & dest,
115  vigra::Rect2D roi);
116  void setAdvancedOptions(const AdvancedOptions& advancedOptions)
117  {
118  m_advancedOptions = advancedOptions;
119  };
120 
121  public:
126  template<class DistImgType>
127  void calcSrcCoordImgs(DistImgType & imgX, DistImgType & imgY);
128 
137  void calcAlpha();
138 
140  template <class ImgIter, class ImgAccessor>
141  void remapImage(vigra::triple<ImgIter, ImgIter, ImgAccessor> srcImg,
142  vigra_ext::Interpolator interpol,
143  AppBase::ProgressDisplay* progress, bool singleThreaded = false);
144 
145 
147  template <class ImgIter, class ImgAccessor,
148  class AlphaIter, class AlphaAccessor>
149  void remapImage(vigra::triple<ImgIter, ImgIter, ImgAccessor> srcImg,
150  std::pair<AlphaIter, AlphaAccessor> alphaImg,
152  AppBase::ProgressDisplay* progress, bool singleThreaded = false);
153 
154 
155  public:
157  vigra::ImageImportInfo::ICCProfile m_ICCProfile;
158 
159  protected:
164 
165 };
166 
167 
168 
171 template <class SrcImgType, class FlatImgType, class DestImgType, class MaskImgType>
172 void remapImage(SrcImgType & srcImg,
173  const MaskImgType & srcAlpha,
174  const FlatImgType & srcFlat,
175  const SrcPanoImage & src,
176  const PanoramaOptions & dest,
177  vigra::Rect2D outputRect,
179  AppBase::ProgressDisplay* progress);
180 
181 
182 } // namespace
183 } // namespace
184 
185 
186 
187 //==============================================================================
188 // templated implementations
189 
190 
194 
195 // #define DEBUG_REMAP 1
196 
197 #ifdef DEBUG_REMAP
198 #include <vigra/impex.hxx> // for vigra::exportImage()
199 #ifdef _WIN32
200 #define DEBUG_FILE_PREFIX "C:/temp/"
201 #else
202 #define DEBUG_FILE_PREFIX "/tmp/"
203 #endif
204 #endif
205 
206 
207 namespace HuginBase {
208 namespace Nona {
209 
210 
211 template <class RemapImage, class AlphaImage>
213  const PanoramaOptions & dest, vigra::Rect2D roi)
214 {
215  // restrict to panorama size
216  m_srcImg = src;
217  m_destImg = dest;
218 
219  if (m_destImg.remapUsingGPU) {
220  // Make width multiple of 8 for fast GPU transfers.
221  const int r = roi.width() % 8;
222  if (r != 0) roi.addSize(vigra::Size2D(8 - r, 0));
223  }
224 
225  Base::resize(roi);
226  m_transf.createTransform(src, dest);
227 
228  DEBUG_DEBUG("after resize: " << Base::m_region);
229  DEBUG_DEBUG("m_srcImg size: " << m_srcImg.getSize());
230 }
231 
232 
233 #if 0
234 
240 template <class RemapImage, class AlphaImage>
241 void RemappedPanoImage<RemapImage,AlphaImage>::setPanoImage(const vigra::Size2D & srcSize,
242  const PanoCommand::VariableMap & srcVars,
243  PanoCommand::Lens::LensProjectionFormat srcProj,
244  const PanoCommand::PanoImage & img,
245  const vigra::Diff2D &destSize,
247  double destHFOV)
248 {
249  m_srcSize = srcSize;
250  m_srcOrigSize.x = img.getWidth();
251  m_srcOrigSize.y = img.getHeight();
252  m_srcProj = m_srcProj;
253 
254 
255  m_srcPanoImg = img;
256  m_destProj = destProj;
257  m_destHFOV = destHFOV;
258  // create transforms
259  // SpaceTransform t;
260  // SpaceTransform invT;
261  /*
262  m_invTransf.createInvTransform(srcSize, srcVars, srcProj,
263  destSize, destProj, destHFOV,
264  m_srcOrigSize);
265  */
266  // calculate ROI for this image.
267  m_transf.createTransform(srcSize, srcVars, srcProj,
268  destSize, destProj, destHFOV,
269  m_srcOrigSize);
270 
271  ImageOptions imgOpts = img.getOptions();
272 
273  // todo: resize crop!
274  bool circCrop = srcProj == Lens::CIRCULAR_FISHEYE;
275  estimateImageRect(destSize, m_srcOrigSize,
276  imgOpts.docrop, imgOpts.cropRect, circCrop,
277  m_transf,
278  imageRect);
279 
280  m_warparound = (destProj == PanoramaOptions::EQUIRECTANGULAR && m_destHFOV == 360);
281 
282 
283 }
284 
285 template <class RemapImage, class AlphaImage>
287  vigra::Size2D srcSize, const HuginBase::PanoramaOptions & opts)
288 {
289  const PanoCommand::PanoImage & img = pano.getImage(imgNr);
290 
291  m_srcSize = srcSize;
292  m_srcOrigSize.x = img.getWidth();
293  m_srcOrigSize.y = img.getHeight();
294  m_srcProj = pano.getLens(pano.getImage(imgNr).getLensNr()).getProjection();
295 
296  m_destProj = opts.getProjection();
297  m_destHFOV = opts.getHFOV();
298  m_warparound = (opts.getProjection() == PanoramaOptions::EQUIRECTANGULAR && opts.getHFOV() == 360);
299 
300  // create transforms
301  // SpaceTransform t;
302  // SpaceTransform invT;
303 
304 // m_invTransf.createInvTransform(pano, imgNr, opts, m_srcSize);
305  m_transf.createTransform(pano, imgNr, opts, m_srcSize);
306 
307  // calculate ROI for this image.
308  m_srcPanoImg = pano.getImage(imgNr);
309  ImageOptions imgOpts = pano.getImage(imgNr).getOptions();
310  vigra::Rect2D imageRect;
311  // todo: resize crop!
312  bool circCrop = pano.getLens(pano.getImage(imgNr).getLensNr()).getProjection() == Lens::CIRCULAR_FISHEYE;
313  estimateImageRect(vigra::Size2D(opts.getWidth(), opts.getHeight()), srcSize,
314  imgOpts.docrop, imgOpts.cropRect, circCrop,
315  m_transf,
316  imageRect);
317 
318 
319  // restrict to panorama size
320  Base::resize(imageRect);
321  DEBUG_DEBUG("after resize: " << Base::m_region);
322 }
323 #endif
324 
325 
330 template<class RemapImage, class AlphaImage>
331 template<class DistImgType>
332 void RemappedPanoImage<RemapImage,AlphaImage>::calcSrcCoordImgs(DistImgType & imgX, DistImgType & imgY)
333 {
334  if (Base::boundingBox().isEmpty()) return;
335  imgX.resize(Base::boundingBox().size().width(), Base::boundingBox().size().height(), vigra::NumericTraits<typename DistImgType::value_type>::max());
336  imgY.resize(Base::boundingBox().size().width(), Base::boundingBox().size().height(), vigra::NumericTraits<typename DistImgType::value_type>::max());
337  // calculate the alpha channel,
338  int xstart = Base::boundingBox().left();
339  int xend = Base::boundingBox().right();
340  int ystart = Base::boundingBox().top();
341  int yend = Base::boundingBox().bottom();
342 
343  // create dist y iterator
344  typename DistImgType::Iterator yImgX(imgX.upperLeft());
345  typename DistImgType::Iterator yImgY(imgY.upperLeft());
346  typename DistImgType::Accessor accX = imgX.accessor();
347  typename DistImgType::Accessor accY = imgY.accessor();
348  // loop over the image and transform
349  for(int y=ystart; y < yend; ++y, ++yImgX.y, ++yImgY.y)
350  {
351  // create x iterators
352  typename DistImgType::Iterator xImgX(yImgX);
353  typename DistImgType::Iterator xImgY(yImgY);
354  for(int x=xstart; x < xend; ++x, ++xImgY.x, ++xImgX.x)
355  {
356  double sx,sy;
357  if (m_transf.transformImgCoord(sx, sy, x, y))
358  {
359  if (m_srcImg.isInside(vigra::Point2D(hugin_utils::roundi(sx), hugin_utils::roundi(sy))))
360  {
361  accX.set(sx, xImgX);
362  accY.set(sy, xImgY);
363  };
364  };
365  }
366  }
367 }
368 
377 template<class RemapImage, class AlphaImage>
379 {
380  if (Base::boundingBox().isEmpty())
381  return;
382 
383  Base::m_mask.resize(Base::boundingBox().size());
384  // calculate the alpha channel,
385  int xstart = Base::boundingBox().left();
386  int xend = Base::boundingBox().right();
387  int ystart = Base::boundingBox().top();
388  int yend = Base::boundingBox().bottom();
389 
390  // loop over the image and transform
391 #pragma omp parallel for schedule(dynamic, 10)
392  for(int y=ystart; y < yend; ++y)
393  {
394  // create dist y iterator
395  typename AlphaImage::Iterator yalpha(Base::m_mask.upperLeft());
396  yalpha.y += y - ystart;
397  // create x iterators
398  typename AlphaImage::Iterator xalpha(yalpha);
399  for(int x=xstart; x < xend; ++x, ++xalpha.x)
400  {
401  double sx,sy;
402  if(m_transf.transformImgCoord(sx,sy,x,y))
403  {
404  if (m_srcImg.isInside(vigra::Point2D(hugin_utils::roundi(sx),hugin_utils::roundi(sy))))
405  {
406  *xalpha = 255;
407  }
408  else
409  {
410  *xalpha = 0;
411  };
412  }
413  else
414  {
415  *xalpha = 0;
416  };
417  }
418  }
419 }
420 
422 template <class ImageType>
423 ImageType CopyImageNewSize(const ImageType& image, const vigra::Size2D& newSize)
424 {
425  ImageType newImage(newSize);
426  vigra::omp::copyImage(vigra::srcImageRange(image, vigra::Rect2D(newSize)), vigra::destImage(newImage));
427  return newImage;
428 };
429 
431 template<class RemapImage, class AlphaImage>
432 template<class ImgIter, class ImgAccessor>
433 void RemappedPanoImage<RemapImage,AlphaImage>::remapImage(vigra::triple<ImgIter, ImgIter, ImgAccessor> srcImg,
434  vigra_ext::Interpolator interpol,
435  AppBase::ProgressDisplay* progress, bool singleThreaded)
436 {
437 
438  // std::ostringstream msg;
439  // msg <<"remapping image " << imgNr;
440  // progress.setMessage(msg.str().c_str());
441 
442  const bool useGPU = m_destImg.remapUsingGPU;
443 
444  if (Base::boundingBox().isEmpty())
445  return;
446 
447  vigra::Diff2D srcImgSize = srcImg.second - srcImg.first;
448 
449  vigra::Size2D expectedSize = m_srcImg.getSize();
450  if (useGPU)
451  {
452  const int r = expectedSize.width() % 8;
453  if (r != 0) expectedSize += vigra::Diff2D(8 - r, 0);
454  }
455 
456  DEBUG_DEBUG("srcImgSize: " << srcImgSize << " m_srcImgSize: " << m_srcImg.getSize());
457  vigra_precondition(srcImgSize == expectedSize,
458  "RemappedPanoImage<RemapImage,AlphaImage>::remapImage(): image unexpectedly changed dimensions.");
459 
460  typedef typename ImgAccessor::value_type input_value_type;
461  typedef typename vigra_ext::ValueTypeTraits<input_value_type>::value_type input_component_type;
462 
463  // setup photometric transform for this image type
464  // this corrects for response curve, white balance, exposure and
465  // radial vignetting
467  invResponse.enforceMonotonicity();
468  if (m_destImg.outputMode == PanoramaOptions::OUTPUT_LDR) {
469  // select exposure and response curve for LDR output
470  std::vector<double> outLut;
471  if (!m_destImg.outputEMoRParams.empty())
472  {
473  vigra_ext::EMoR::createEMoRLUT(m_destImg.outputEMoRParams, outLut);
474  };
476  if (!m_destImg.outputPixelType.empty()) {
477  maxVal = vigra_ext::getMaxValForPixelType(m_destImg.outputPixelType);
478  }
479 
480  invResponse.setOutput(1.0/pow(2.0,m_destImg.outputExposureValue), outLut,
481  maxVal, m_destImg.outputRangeCompression);
482  } else {
483  invResponse.setHDROutput(true,1.0/pow(2.0,m_destImg.outputExposureValue));
484  }
485 
486  if ((m_srcImg.hasActiveMasks()) || (m_srcImg.getCropMode() != SrcPanoImage::NO_CROP) || Nona::GetAdvancedOption(m_advancedOptions, "maskClipExposure", false))
487  {
488  // need to create and additional alpha image for the crop mask...
489  // not very efficient during the remapping phase, but works.
490  vigra::BImage alpha(srcImgSize.x, srcImgSize.y);
491 
492  switch (m_srcImg.getCropMode()) {
494  {
495  if (useGPU) {
496  if (srcImgSize != m_srcImg.getSize()) {
497  // src image with was increased for alignment reasons.
498  // Need to make an alpha image to mask off the extended region.
499  initImage(vigra::destImageRange(alpha),0);
500  initImage(alpha.upperLeft(),
501  alpha.upperLeft()+m_srcImg.getSize(),
502  alpha.accessor(),255);
503  }
504  else
505  initImage(vigra::destImageRange(alpha),255);
506  }
507  else
508  initImage(vigra::destImageRange(alpha),255);
509  break;
510  }
512  {
513  vigra::Rect2D cR = m_srcImg.getCropRect();
514  hugin_utils::FDiff2D m( (cR.left() + cR.width()/2.0),
515  (cR.top() + cR.height()/2.0) );
516 
517  double radius = std::min(cR.width(), cR.height())/2.0;
518  // Default the entire alpha channel to opaque..
519  initImage(vigra::destImageRange(alpha),255);
520  //..crop everything outside the circle
522  break;
523  }
525  {
526  vigra::Rect2D cR = m_srcImg.getCropRect();
527  // Default the entire alpha channel to transparent..
528  initImage(vigra::destImageRange(alpha),0);
529  // Make sure crop is inside the image..
530  cR &= vigra::Rect2D(0,0, srcImgSize.x, srcImgSize.y);
531  // Opaque only the area within the crop rectangle..
532  initImage(alpha.upperLeft()+cR.upperLeft(),
533  alpha.upperLeft()+cR.lowerRight(),
534  alpha.accessor(),255);
535  break;
536  }
537  default:
538  break;
539  }
540  if(m_srcImg.hasActiveMasks())
541  vigra_ext::applyMask(vigra::destImageRange(alpha), m_srcImg.getActiveMasks());
542  if (Nona::GetAdvancedOption(m_advancedOptions, "maskClipExposure", false))
543  {
544  const float lowerCutoff = Nona::GetAdvancedOption(m_advancedOptions, "maskClipExposureLowerCutoff", NONA_DEFAULT_EXPOSURE_LOWER_CUTOFF);
545  const float upperCutoff = Nona::GetAdvancedOption(m_advancedOptions, "maskClipExposureUpperCutoff", NONA_DEFAULT_EXPOSURE_UPPER_CUTOFF);
546  vigra_ext::applyExposureClipMask(srcImg, vigra::destImageRange(alpha), lowerCutoff, upperCutoff);
547  };
548  if (useGPU) {
549  transformImageAlphaGPU(srcImg,
550  vigra::srcImage(alpha),
551  destImageRange(Base::m_image),
552  destImage(Base::m_mask),
553  Base::boundingBox().upperLeft(),
554  m_transf,
555  invResponse,
556  m_srcImg.horizontalWarpNeeded(),
557  interpol,
558  progress);
559  if (Base::boundingBox().right() > m_destImg.getROI().right())
560  {
561  // dest image was enlarged for GPU alignment issue
562  // delete the pixels outside
563  vigra::Rect2D newBoundingBox = Base::boundingBox() & m_destImg.getROI();
564  Base::m_image = CopyImageNewSize(Base::m_image, newBoundingBox.size());
565  Base::m_mask = CopyImageNewSize(Base::m_mask, newBoundingBox.size());
566  Base::m_region = newBoundingBox;
567  };
568  } else {
569  transformImageAlpha(srcImg,
570  vigra::srcImage(alpha),
571  destImageRange(Base::m_image),
572  destImage(Base::m_mask),
573  Base::boundingBox().upperLeft(),
574  m_transf,
575  invResponse,
576  m_srcImg.horizontalWarpNeeded(),
577  interpol,
578  progress,
579  singleThreaded);
580  }
581  } else {
582  if (useGPU) {
583  if (srcImgSize != m_srcImg.getSize()) {
584  // src image with was increased for alignment reasons.
585  // Need to make an alpha image to mask off the extended region.
586  vigra::BImage alpha(srcImgSize.x, srcImgSize.y, vigra::UInt8(0));
587  initImage(alpha.upperLeft(),
588  alpha.upperLeft()+m_srcImg.getSize(),
589  alpha.accessor(),255);
590  transformImageAlphaGPU(srcImg,
591  vigra::srcImage(alpha),
592  destImageRange(Base::m_image),
593  destImage(Base::m_mask),
594  Base::boundingBox().upperLeft(),
595  m_transf,
596  invResponse,
597  m_srcImg.horizontalWarpNeeded(),
598  interpol,
599  progress);
600 
601  }
602  else {
603  transformImageGPU(srcImg,
604  destImageRange(Base::m_image),
605  destImage(Base::m_mask),
606  Base::boundingBox().upperLeft(),
607  m_transf,
608  invResponse,
609  m_srcImg.horizontalWarpNeeded(),
610  interpol,
611  progress);
612  }
613  if (Base::boundingBox().right() > m_destImg.getROI().right())
614  {
615  // dest image was enlarged for GPU alignment issue
616  // delete the pixels outside
617  vigra::Rect2D newBoundingBox = Base::boundingBox() & m_destImg.getROI();
618  Base::m_image = CopyImageNewSize(Base::m_image, newBoundingBox.size());
619  Base::m_mask = CopyImageNewSize(Base::m_mask, newBoundingBox.size());
620  Base::m_region = newBoundingBox;
621  };
622  } else {
623  transformImage(srcImg,
624  destImageRange(Base::m_image),
625  destImage(Base::m_mask),
626  Base::boundingBox().upperLeft(),
627  m_transf,
628  invResponse,
629  m_srcImg.horizontalWarpNeeded(),
630  interpol,
631  progress,
632  singleThreaded);
633  }
634  }
635 }
636 
637 
638 
640 template<class RemapImage, class AlphaImage>
641 template<class ImgIter, class ImgAccessor,
642  class AlphaIter, class AlphaAccessor>
643 void RemappedPanoImage<RemapImage,AlphaImage>::remapImage(vigra::triple<ImgIter, ImgIter, ImgAccessor> srcImg,
644  std::pair<AlphaIter, AlphaAccessor> alphaImg,
646  AppBase::ProgressDisplay* progress, bool singleThreaded)
647 {
648  const bool useGPU = m_destImg.remapUsingGPU;
649 
650  if (Base::boundingBox().isEmpty())
651  return;
652 
653  progress->setMessage("remapping", hugin_utils::stripPath(m_srcImg.getFilename()));
654 
655  vigra::Diff2D srcImgSize = srcImg.second - srcImg.first;
656 
657  vigra::Size2D expectedSize = m_srcImg.getSize();
658  if (useGPU)
659  {
660  const int r = expectedSize.width() % 8;
661  if (r != 0) expectedSize += vigra::Diff2D(8 - r, 0);
662  }
663  vigra_precondition(srcImgSize == expectedSize,
664  "RemappedPanoImage<RemapImage,AlphaImage>::remapImage(): image unexpectedly changed dimensions.");
665 
666  typedef typename ImgAccessor::value_type input_value_type;
667  typedef typename vigra_ext::ValueTypeTraits<input_value_type>::value_type input_component_type;
668 
669  // setup photometric transform for this image type
670  // this corrects for response curve, white balance, exposure and
671  // radial vignetting
673  if (m_destImg.outputMode == PanoramaOptions::OUTPUT_LDR) {
674  // select exposure and response curve for LDR output
675  std::vector<double> outLut;
676  // scale up to desired output format
678  if (!m_destImg.outputPixelType.empty()) {
679  maxVal = vigra_ext::getMaxValForPixelType(m_destImg.outputPixelType);
680  }
681  if (!m_destImg.outputEMoRParams.empty())
682  {
683  vigra_ext::EMoR::createEMoRLUT(m_destImg.outputEMoRParams, outLut);
685  };
686  invResponse.setOutput(1.0/pow(2.0,m_destImg.outputExposureValue), outLut,
687  maxVal, m_destImg.outputRangeCompression);
688  } else {
689  invResponse.setHDROutput(true,1.0/pow(2.0,m_destImg.outputExposureValue));
690  }
691 
692  if ((m_srcImg.hasActiveMasks()) || (m_srcImg.getCropMode() != SrcPanoImage::NO_CROP) || Nona::GetAdvancedOption(m_advancedOptions, "maskClipExposure", false)) {
693  vigra::BImage alpha(srcImgSize);
694  vigra::Rect2D cR = m_srcImg.getCropRect();
695  switch (m_srcImg.getCropMode()) {
697  {
698  // Just copy the source alpha channel and crop it down.
699  vigra::copyImage(vigra::make_triple(alphaImg.first,
700  alphaImg.first + srcImgSize, alphaImg.second),
701  vigra::destImage(alpha));
702  break;
703  }
705  {
706  // Just copy the source alpha channel and crop it down.
707  vigra::copyImage(vigra::make_triple(alphaImg.first,
708  alphaImg.first + srcImgSize, alphaImg.second),
709  vigra::destImage(alpha));
710  hugin_utils::FDiff2D m( (cR.left() + cR.width()/2.0),
711  (cR.top() + cR.height()/2.0) );
712  double radius = std::min(cR.width(), cR.height())/2.0;
714  break;
715  }
717  {
718  // Intersect the cropping rectangle with the one from the base
719  // image to ensure it fits inside.
720  cR &= vigra::Rect2D(0,0, srcImgSize.x, srcImgSize.y);
721 
722  // Start with a blank alpha channel for the destination
723  initImage(vigra::destImageRange(alpha),0);
724 
725  // Copy in only the area inside the rectangle
726  vigra::copyImage(alphaImg.first + cR.upperLeft(),
727  alphaImg.first + cR.lowerRight(),
728  alphaImg.second,
729  alpha.upperLeft() + cR.upperLeft(),alpha.accessor());
730  break;
731  }
732  default:
733  break;
734  }
735  if(m_srcImg.hasActiveMasks())
736  vigra_ext::applyMask(vigra::destImageRange(alpha), m_srcImg.getActiveMasks());
737  if (Nona::GetAdvancedOption(m_advancedOptions, "maskClipExposure", false))
738  {
739  const float lowerCutoff = Nona::GetAdvancedOption(m_advancedOptions, "maskClipExposureLowerCutoff", NONA_DEFAULT_EXPOSURE_LOWER_CUTOFF);
740  const float upperCutoff = Nona::GetAdvancedOption(m_advancedOptions, "maskClipExposureUpperCutoff", NONA_DEFAULT_EXPOSURE_UPPER_CUTOFF);
741  vigra_ext::applyExposureClipMask(srcImg, vigra::destImageRange(alpha), lowerCutoff, upperCutoff);
742  };
743  if (useGPU) {
745  vigra::srcImage(alpha),
746  destImageRange(Base::m_image),
747  destImage(Base::m_mask),
748  Base::boundingBox().upperLeft(),
749  m_transf,
750  invResponse,
751  m_srcImg.horizontalWarpNeeded(),
752  interp,
753  progress);
754  if (Base::boundingBox().right() > m_destImg.getROI().right())
755  {
756  // dest image was enlarged for GPU alignment issue
757  // delete the pixels outside
758  vigra::Rect2D newBoundingBox = Base::boundingBox() & m_destImg.getROI();
759  Base::m_image = CopyImageNewSize(Base::m_image, newBoundingBox.size());
760  Base::m_mask = CopyImageNewSize(Base::m_mask, newBoundingBox.size());
761  Base::m_region = newBoundingBox;
762  };
763  } else {
765  vigra::srcImage(alpha),
766  destImageRange(Base::m_image),
767  destImage(Base::m_mask),
768  Base::boundingBox().upperLeft(),
769  m_transf,
770  invResponse,
771  m_srcImg.horizontalWarpNeeded(),
772  interp,
773  progress,
774  singleThreaded);
775  }
776  } else {
777  if (useGPU) {
778  // extended region (if any) should already be cleared since ImportImageAlpha shouldn't have touched it.
780  alphaImg,
781  destImageRange(Base::m_image),
782  destImage(Base::m_mask),
783  Base::boundingBox().upperLeft(),
784  m_transf,
785  invResponse,
786  m_srcImg.horizontalWarpNeeded(),
787  interp,
788  progress);
789  if (Base::boundingBox().right() > m_destImg.getROI().right())
790  {
791  // dest image was enlarged for GPU alignment issue
792  // delete the pixels outside
793  vigra::Rect2D newBoundingBox = Base::boundingBox() & m_destImg.getROI();
794  Base::m_image = CopyImageNewSize(Base::m_image, newBoundingBox.size());
795  Base::m_mask = CopyImageNewSize(Base::m_mask, newBoundingBox.size());
796  Base::m_region = newBoundingBox;
797  };
798  } else {
800  alphaImg,
801  destImageRange(Base::m_image),
802  destImage(Base::m_mask),
803  Base::boundingBox().upperLeft(),
804  m_transf,
805  invResponse,
806  m_srcImg.horizontalWarpNeeded(),
807  interp,
808  progress,
809  singleThreaded);
810  }
811  }
812 }
813 
814 
815 
816 
817 
818 
821 template <class SrcImgType, class FlatImgType, class DestImgType, class MaskImgType>
822 void remapImage(SrcImgType & srcImg,
823  const MaskImgType & srcAlpha,
824  const FlatImgType & srcFlat,
825  const SrcPanoImage & src,
826  const PanoramaOptions & dest,
827  vigra::Rect2D outputROI,
828 // vigra_ext::Interpolator interpolator,
830  AppBase::ProgressDisplay* progress)
831 {
832 #ifdef DEBUG_REMAP
833  {
834  vigra::ImageExportInfo exi( DEBUG_FILE_PREFIX "hugin03_BeforeRemap.tif");
835  vigra::exportImage(vigra::srcImageRange(srcImg), exi);
836  }
837  {
838  if (srcAlpha.width() > 0) {
839  vigra::ImageExportInfo exi(DEBUG_FILE_PREFIX "hugin04_BeforeRemapAlpha.tif");
840  vigra::exportImage(vigra::srcImageRange(srcAlpha), exi);
841  }
842  }
843 #endif
844 
845  progress->setMessage("remapping", hugin_utils::stripPath(src.getFilename()));
846  // set pano image
847  DEBUG_DEBUG("setting src image with size: " << src.getSize());
848  remapped.setPanoImage(src, dest, outputROI);
849  // TODO: add provide support for flatfield images.
850  if (srcAlpha.size().x > 0) {
851  remapped.remapImage(vigra::srcImageRange(srcImg),
852  vigra::srcImage(srcAlpha), dest.interpolator,
853  progress);
854  } else {
855  remapped.remapImage(vigra::srcImageRange(srcImg), dest.interpolator, progress);
856  }
857 
858 #ifdef DEBUG_REMAP
859  {
860  vigra::ImageExportInfo exi( DEBUG_FILE_PREFIX "hugin04_AfterRemap.tif");
861  vigra::exportImage(vigra::srcImageRange(remapped.m_image), exi);
862  }
863  {
864  vigra::ImageExportInfo exi(DEBUG_FILE_PREFIX "hugin04_AfterRemapAlpha.tif");
865  vigra::exportImage(vigra::srcImageRange(remapped.m_mask), exi);
866  }
867 #endif
868 }
869 
870 
871 } //namespace
872 } //namespace
873 
874 #endif // _H
AlphaImage m_mask
corresponding alpha channel
Definition: ROIImage.h:274
PanoramaOptions::ProjectionFormat getProjection() const
double getMaxValForPixelType(const std::string &v)
Definition: utils.h:89
RemappedPanoImage()
create a remapped pano image
bool GetAdvancedOption(const AdvancedOptions &opts, const std::string &name, const bool defaultValue)
check if given option is saved and return its boolean value, otherwise return defaultValue ...
void applyMask(vigra::triple< SrcImageIterator, SrcImageIterator, SrcAccessor > img, HuginBase::MaskPolygonVector masks)
Definition: Mask.h:160
AlphaImage::const_traverser const_mask_traverser
int roundi(T x)
Definition: hugin_math.h:73
void transformImage(vigra::triple< SrcImageIterator, SrcImageIterator, SrcAccessor > src, vigra::triple< DestImageIterator, DestImageIterator, DestAccessor > dest, std::pair< AlphaImageIterator, AlphaAccessor > alpha, vigra::Diff2D destUL, TRANSFORM &transform, PixelTransform &pixelTransform, bool warparound, Interpolator interpol, AppBase::ProgressDisplay *progress, bool singleThreaded=false)
Transform an image into the panorama.
void setPanoImage(const SrcPanoImage &src, const PanoramaOptions &dest, vigra::Rect2D roi)
void remapImage(SrcImgType &srcImg, const MaskImgType &srcAlpha, const FlatImgType &srcFlat, const SrcPanoImage &src, const PanoramaOptions &dest, vigra::Rect2D outputRect, RemappedPanoImage< DestImgType, MaskImgType > &remapped, AppBase::ProgressDisplay *progress)
remap a single image
radiometric transformation, includes exposure, vignetting and white balance
unsigned int getHeight() const
get panorama height
void estimateImageRect(const SrcPanoImage &src, const PanoramaOptions &dest, TRANSFORM &transf, vigra::Rect2D &imgRect)
calculate the outline of the image
void enforceMonotonicity(LUT &lut)
enforce monotonicity of an array (mostly used for lookup tables)
Definition: lut.h:87
declaration of classes to work with mask
vigra::pair< typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::ImageConstAccessor > srcImage(const ROIImage< Image, Mask > &img)
Definition: ROIImage.h:300
RemapImage::value_type image_value_type
void transformImageGPU(vigra::triple< SrcImageIterator, SrcImageIterator, SrcAccessor > src, vigra::triple< DestImageIterator, DestImageIterator, DestAccessor > dest, std::pair< AlphaImageIterator, AlphaAccessor > alpha, vigra::Diff2D destUL, TRANSFORM &transform, PixelTransform &pixelTransform, bool warparound, Interpolator interpol, AppBase::ProgressDisplay *progress)
Transform an image into the panorama.
vigra_ext::ValueTypeTraits< image_value_type >::value_type component_type
Model for a panorama.
Definition: Panorama.h:152
void calcSrcCoordImgs(DistImgType &imgX, DistImgType &imgY)
calculate distance map.
void setHDROutput(bool hdrMode, double destExposure)
vigra::FRGBImage ImageType
vigra_ext::Interpolator interpolator
float pow(float a, double b)
Definition: utils.h:181
int getWidth() const
Get the width of the image in pixels.
Definition: SrcPanoImage.h:266
void remapImage(vigra::triple< ImgIter, ImgIter, ImgAccessor > srcImg, vigra_ext::Interpolator interpol, AppBase::ProgressDisplay *progress, bool singleThreaded=false)
remap a image without alpha channel
Helper class for storing different options.
vigra::ImageImportInfo::ICCProfile m_ICCProfile
void applyExposureClipMask(vigra::triple< ImgIter, ImgIter, ImgAccessor > image, vigra::triple< AlphaIter, AlphaIter, AlphaAccessor > mask, double lowerLimit, double upperLimit)
Definition: utils.h:370
void setMessage(const std::string &message, const std::string &filename="")
sets the message to given string
vigra::pair< typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::ImageAccessor > destImage(ROIImage< Image, Alpha > &img)
Definition: ROIImage.h:324
AlphaImage::ConstAccessor ConstMaskAccessor
Contains functions to transform whole images.
vigra::triple< typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::ImageConstAccessor > srcImageRange(const ROIImage< Image, Mask > &img)
helper function for ROIImages
Definition: ROIImage.h:287
vigra_ext::ROIImage< RemapImage, AlphaImage > Base
void createEMoRLUT(const std::vector< float > &params, VECTOR &lut)
Definition: emor.h:44
std::map< std::string, Variable > VariableMap
RemapImage::const_traverser const_image_traverser
unsigned int getWidth() const
void transformImageAlpha(vigra::triple< SrcImageIterator, SrcImageIterator, SrcAccessor > src, std::pair< SrcAlphaIterator, SrcAlphaAccessor > srcAlpha, vigra::triple< DestImageIterator, DestImageIterator, DestAccessor > dest, std::pair< AlphaImageIterator, AlphaAccessor > alpha, vigra::Diff2D destUL, TRANSFORM &transform, PixelTransform &pixelTransform, bool warparound, Interpolator interpol, AppBase::ProgressDisplay *progress, bool singleThreaded=false)
Transform image, and respect a possible alpha channel.
AlphaImage::traverser mask_traverser
static T max(T x, T y)
Definition: svm.cpp:65
Holds transformations for Image -&gt; Pano and the other way.
#define DEBUG_DEBUG(msg)
Definition: utils.h:68
AlphaImage::value_type mask_value_type
ImageType CopyImageNewSize(const ImageType &image, const vigra::Size2D &newSize)
copies image into new image with changed size
void estimateImageAlpha(const SrcPanoImage &src, const PanoramaOptions &dest, TRANSFORM &transf, vigra::Rect2D &imgRect, vigra::BImage &alpha, double &scale)
std::map< std::string, std::string > AdvancedOptions
RemapImage::ConstAccessor ConstImageAccessor
void resize(double x_dest, double y_dest, double *x_src, double *y_src, const _FuncParams &params)
vigra::triple< typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::ImageAccessor > destImageRange(ROIImage< Image, Alpha > &img)
Definition: ROIImage.h:312
void copyImage(SrcImageIterator src_upperleft, SrcImageIterator src_lowerright, SrcAccessor src_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc)
Definition: openmp_vigra.h:305
#define NONA_DEFAULT_EXPOSURE_LOWER_CUTOFF
void calcAlpha()
calculate only the alpha channel.
brief description.
Definition: ROIImage.h:85
const SrcPanoImage & getImage(std::size_t nr) const
get a panorama image, counting starts with 0
Definition: Panorama.h:211
void setAdvancedOptions(const AdvancedOptions &advancedOptions)
T1::value_type value_type
Definition: utils.h:153
ProjectionFormat
Projection of final panorama.
Interpolator
enum with all interpolation methods
Definition: Interpolators.h:78
All variables of a source image.
Definition: SrcPanoImage.h:194
Panorama image options.
struct to hold a image state for stitching
static T min(T x, T y)
Definition: svm.cpp:62
void transformImageAlphaGPU(vigra::triple< SrcImageIterator, SrcImageIterator, SrcAccessor > src, std::pair< SrcAlphaIterator, SrcAlphaAccessor > srcAlpha, vigra::triple< DestImageIterator, DestImageIterator, DestAccessor > dest, std::pair< AlphaImageIterator, AlphaAccessor > alpha, vigra::Diff2D destUL, TRANSFORM &transform, PixelTransform &pixelTransform, bool warparound, Interpolator interpol, AppBase::ProgressDisplay *progress)
Transform image, and respect a possible alpha channel.
void setOutput(double destExposure, const LUTD &destLut, double scale, double rangeCompression=0.0)
output lut
std::string stripPath(const std::string &filename)
remove the path of a filename (mainly useful for gui display of filenames)
Definition: utils.cpp:160
#define NONA_DEFAULT_EXPOSURE_UPPER_CUTOFF
RemapImage::traverser image_traverser
void circularCrop(vigra::triple< SrcImageIterator, SrcImageIterator, SrcAccessor > img, hugin_utils::FDiff2D middle, double radius)
Apply a circular crop to img.
Definition: utils.h:495
Contains functions to transform whole images.