28 #ifndef POISSON_BLEND_H
29 #define POISSON_BLEND_H
32 #include <vigra/stdimage.hxx>
33 #include <vigra/convolution.hxx>
34 #include <vigra/stdconvolution.hxx>
35 #include <vigra/basicgeometry.hxx>
49 template <
class Image,
class Mask>
50 inline typename vigra::NumericTraits<typename Image::PixelType>::RealPromote
ProcessNeighborPixels(
const int x,
const int y,
const int dx,
const int dy,
const Image& image,
const Mask& mask)
52 const typename Mask::PixelType m1 = mask[y + dy][x + dx];
53 const typename Mask::PixelType m2 = mask[y - dy][x - dx];
56 return image[y + dy][x + dx] + image[y - dy][x - dx];
62 return 2 * image[y + dy][x + dx];
66 return 2 * image[y - dy][x - dx];
70 template <
class Image,
class Mask,
class SeamMask>
71 inline typename vigra::NumericTraits<typename Image::PixelType>::RealPromote
ProcessBorderPixel(
const int x,
const int y,
const int dx,
const int dy,
const Image& image,
const Mask& mask,
const SeamMask& seam)
73 const typename SeamMask::PixelType seam1 = seam[y + dy][x + dx];
74 const typename SeamMask::PixelType seam2 = seam[y - dy][x - dx];
75 const typename Mask::PixelType mask1 = mask[y + dy][x + dx];
76 const typename Mask::PixelType mask2 = mask[y - dy][x - dx];
77 if (seam1 > 0 && seam2 > 0)
79 if (mask1 > 0 && mask2 > 0)
81 return image[y + dy][x + dx] + image[y - dy][x - dx];
85 return 2 *image[y + dy][x + dx];
89 return 2 * image[y - dy][x - dx];
96 return 2 * image[y + dy][x + dx];
100 return vigra::NumericTraits<typename vigra::NumericTraits<typename Image::PixelType>::RealPromote>::zero();
107 return 2 * image[y - dy][x - dx];
111 return vigra::NumericTraits<typename vigra::NumericTraits<typename Image::PixelType>::RealPromote>::zero();
114 return vigra::NumericTraits<typename vigra::NumericTraits<typename Image::PixelType>::RealPromote>::zero();
117 template <
class Image,
class SeamMask>
118 inline typename Image::PixelType
GetBorderGradient(
const int x,
const int y,
const int dx,
const int dy,
const SeamMask& seams,
const Image& image1,
const vigra::Point2D& offset)
120 if (seams[y + dy][x + dx] == 1)
122 return image1[offset.y + y + dy][offset.x + x + dx];
124 return vigra::NumericTraits<typename Image::PixelType>::zero();
127 template <
class Image,
class SeamMask>
128 inline typename vigra::NumericTraits<typename Image::PixelType>::RealPromote
GetBorderValues(
const int x,
const int y,
int dx,
int dy,
const Image& image,
const SeamMask& seams)
130 const typename SeamMask::PixelType s1 = seams[y + dy][x + dx];
131 const typename SeamMask::PixelType s2 = seams[y - dy][x - dx];
132 if (s1 > 1 && s2 > 1)
134 return image[y + dy][x + dx] + image[y - dy][x - dx];
138 return (2 - std::min<int>(s2, 2))*image[y + dy][x + dx] + (2 - std::min<int>(s1, 2))*image[y - dy][x - dx];
142 template <
class Image>
146 vigra::Kernel2D<double> filter2D;
147 filter2D.initExplicitly(vigra::Diff2D(-1, -1), vigra::Diff2D(1, 1)) = 0.25, 0.5, 0.25, 0.5, 1, 0.5, 0.25, 0.5, 0.25;
148 Image smoothImage(in.size());
159 const int v =
static_cast<int>(vf);
168 if (v == 85 || v == 89 || v == 93 || v == 97)
186 template <
class Image1,
class Image2>
189 vigra_precondition(image1.size() == image2.size(),
"ConvolveImage: Image size does not match");
190 vigra_precondition(image1.width() >= 2 && image1.height() >= 2,
"ConvolveImage: Image too small");
191 const int width = image1.width();
192 const int height = image1.height();
194 image2[0][0] = factor1*image1[0][0] + factor2*image1[0][1] + factor2*image1[1][0];
195 for (
int x = 1; x < width - 1; ++x)
197 image2[0][x] = factor1*image1[0][x] + factor2*image1[0][x - 1] + factor2*image1[0][x + 1] + factor2*image1[1][x];
199 image2[0][width - 1] = factor1*image1[0][width - 1] + factor2*image1[0][width - 2] + factor2*image1[1][width - 1];
200 #pragma omp parallel for
201 for (
int y = 1; y < height - 1; ++y)
203 image2[y][0] = factor1*image1[y][0] + factor2*image1[y - 1][0]
204 + factor2*image1[y][1] + factor2*image1[y + 1][0];
205 for (
size_t x = 1; x < width - 1; ++x)
207 image2[y][x] = factor1*image1[y][x] + factor2*image1[y - 1][x]
208 + factor2*image1[y][x - 1] + factor2*image1[y + 1][x] + factor2*image1[y][x + 1];
210 image2[y][width - 1] = factor1*image1[y][width - 1] + factor2*image1[y - 1][width - 1]
211 + factor2*image1[y][width - 2] + factor2*image1[y + 1][width - 1];
214 image2[height - 1][0] = factor1*image1[height - 1][0] + factor2*image1[height - 1][1] + factor2*image1[height - 2][0];
215 for (
size_t x = 1; x < width - 1; ++x)
217 image2[height - 1][x] = factor1*image1[height - 1][x] + factor2*image1[height - 1][x - 1] + factor2*image1[height - 1][x + 1] + factor2*image1[height - 2][x];
219 image2[height - 1][width - 1] = factor1*image1[height - 1][width - 1] + factor2*image1[height - 1][width - 2] + factor2*image1[height - 2][width - 1];
234 template <
class Image>
237 vigra::Int8Image output(input.size());
243 template <
class ComponentType>
246 template <
class ComponentType>
247 double GetRealValue(
const vigra::RGBValue<ComponentType>& val) {
return val.magnitude(); }
249 template <
class Image,
class SeamMask>
250 void SOR(Image& target,
const Image& gradient,
const SeamMask& seams,
const float omega,
const float errorThreshold,
const int maxIter,
const bool doWrap)
252 typedef typename Image::PixelType TargetPixelType;
253 const int width = target.width();
254 const int height = target.height();
258 for (
int j = 0; j < maxIter; j++)
267 const TargetPixelType delta = omega * ((gradient[0][0] + target[0][1] + 2 * target[1][0] + target[0][width - 1]) / 4.0f - target[0][0]);
269 target[0][0] += delta;
273 const TargetPixelType delta = omega * ((gradient[0][0] + 2 * target[0][1] + 2 * target[1][0]) / 4.0f - target[0][0]);
275 target[0][0] += delta;
278 for (
int x = 1; x < width - 1; ++x)
282 const TargetPixelType delta = omega * ((gradient[0][x] +
detail::GetBorderValues(x, 0, 1, 0, target, seams) + 2 * target[1][x]) / 4.0f - target[0][x]);
284 target[0][x] += delta;
287 if (seams[0][width - 1] > 1)
291 const TargetPixelType delta = omega * ((gradient[0][width - 1] + target[0][width - 2] + 2 * target[1][width - 1] + target[0][0]) / 4.0f - target[0][width - 1]);
293 target[0][width - 1] += delta;
297 const TargetPixelType delta = omega * ((gradient[0][width - 1] + 2 * target[0][width - 2] + 2 * target[1][width - 1]) / 4.0f - target[0][width - 1]);
299 target[0][width - 1] += delta;
302 #pragma omp parallel for reduction(+: error) schedule(dynamic, 100)
303 for (
int y = 1; y < height - 1; ++y)
310 + target[y][1] + target[y][width - 1]) / 4.0f - target[y][0]);
312 target[y][0] += delta;
316 const TargetPixelType delta = omega * ((gradient[y][0] +
detail::GetBorderValues(0, y, 0, 1, target, seams) + 2 * target[y][1]) / 4.0f - target[y][0]);
318 target[y][0] += delta;
321 for (
int x = 1; x < width - 1; ++x)
323 const typename SeamMask::value_type maskValue = seams[y][x];
329 const TargetPixelType sum =
detail::GetBorderValues(x, y, 1, 0, target, seams) +
detail::GetBorderValues(x, y, 0, 1, target, seams);
330 const TargetPixelType delta = omega * ((gradient[y][x] + sum) / 4.0f - target[y][x]);
332 target[y][x] += delta;
336 const TargetPixelType sum = target[y + 1][x] + target[y][x + 1] + target[y - 1][x] + target[y][x - 1];
337 const TargetPixelType delta = omega * ((gradient[y][x] + sum) / 4.0f - target[y][x]);
339 target[y][x] += delta;
343 if (seams[y][width - 1] > 1)
347 const TargetPixelType delta = omega * ((gradient[y][width - 1] +
detail::GetBorderValues(width - 1, y, 0, 1, target, seams) + target[y][width - 2] + target[y][0]) / 4.0f - target[y][width - 1]);
349 target[y][width - 1] += delta;
353 const TargetPixelType delta = omega * ((gradient[y][width - 1] +
detail::GetBorderValues(width - 1, y, 0, 1, target, seams) + 2 * target[y][width - 2]) / 4.0f - target[y][width - 1]);
355 target[y][width - 1] += delta;
360 if (seams[height - 1][0] > 1)
364 const TargetPixelType delta = omega * ((gradient[height - 1][0] + 2 * target[height - 2][0] + target[height - 1][1] + target[height - 1][width - 1]) / 4.0f - target[height - 1][0]);
366 target[height - 1][0] += delta;
370 const TargetPixelType delta = omega * ((gradient[height - 1][0] + 2 * target[height - 2][0] + 2 * target[height - 1][1]) / 4.0f - target[height - 1][0]);
372 target[height - 1][0] += delta;
375 for (
int x = 1; x < width - 1; ++x)
377 if (seams[height - 1][x] > 1)
379 const TargetPixelType delta = omega * ((gradient[height - 1][x] +
detail::GetBorderValues(x, height - 1, 1, 0, target, seams) + 2 * target[height - 2][x]) / 4.0f - target[height - 1][x]);
381 target[height - 1][x] += delta;
384 if (seams[height - 1][width - 1] > 1)
388 const TargetPixelType delta = omega * ((gradient[height - 1][width - 1] + 2 * target[height - 2][width - 1] + target[height - 1][width - 2] + target[height - 1][0]) / 4.0f - target[height - 1][width - 1]);
390 target[height - 1][width - 1] += delta;
394 const TargetPixelType delta = omega * ((gradient[height - 1][width - 1] + 2 * target[height - 2][width - 1] + 2 * target[height - 1][width - 2]) / 4.0f - target[height - 1][width - 1]);
396 target[height - 1][width - 1] += delta;
400 if (oldError > 0 &&
log(oldError / error) /
log(10.0) < errorThreshold)
408 template <
class Image,
class SeamMask>
409 void CalcResidualError(Image& error,
const Image& target,
const Image& gradient,
const SeamMask& seam,
const bool doWrap)
411 typedef typename Image::PixelType ImagePixelType;
412 const int width = target.width();
413 const int height = target.height();
419 const ImagePixelType sum = 2 * target[1][0] + target[0][1] + target[0][width - 1];
420 error[0][0] = (4 * target[0][0] - sum - gradient[0][0]);
424 const ImagePixelType sum = 2 * target[1][0] + 2 * target[0][1];
425 error[0][0] = (4 * target[0][0] - sum - gradient[0][0]);
428 for (
int x = 1; x < width - 1; ++x)
433 error[0][x] = (4 * target[0][x] - sum - gradient[0][x]);
436 if (seam[0][width - 1] > 1)
440 const ImagePixelType sum = 2 * target[1][width - 1] + target[0][width - 2] + target[0][0];
441 error[0][width - 1] = (4 * target[0][width - 1] - sum - gradient[0][width - 1]);
445 const ImagePixelType sum = 2 * target[1][width - 1] + 2 * target[0][width - 2];
446 error[0][width - 1] = (4 * target[0][width - 1] - sum - gradient[0][width - 1]);
449 #pragma omp parallel for schedule(dynamic, 100)
450 for (
int y = 1; y < height - 1; ++y)
456 const ImagePixelType sum =
detail::GetBorderValues(0, y, 0, 1, target, seam) + target[y][1] + target[y][width - 1];
457 error[y][0] = (4 * target[y][0] - sum - gradient[y][0]);
462 error[y][0] = (4 * target[y][0] - sum - gradient[y][0]);
465 for (
int x = 1; x < width - 1; ++x)
467 const typename SeamMask::value_type maskValue = seam[y][x];
473 const ImagePixelType sum =
detail::GetBorderValues(x, y, 1, 0, target, seam) +
detail::GetBorderValues(x, y, 0, 1, target, seam);
474 error[y][x] = (4 * target[y][x] - sum - gradient[y][x]);
478 const ImagePixelType sum = target[y + 1][x] + target[y][x + 1] + target[y - 1][x] + target[y][x - 1];
479 error[y][x] = (4 * target[y][x] - sum - gradient[y][x]);
483 if (seam[y][width - 1] > 1)
487 const ImagePixelType sum =
detail::GetBorderValues(width - 1, y, 0, 1, target, seam) + target[y][width - 2] + target[y][0];
488 error[y][width - 1] = (4 * target[y][width - 1] - sum - gradient[y][width - 1]);
492 const ImagePixelType sum =
detail::GetBorderValues(width - 1, y, 0, 1, target, seam) + 2 * target[y][width - 2];
493 error[y][width - 1] = (4 * target[y][width - 1] - sum - gradient[y][width - 1]);
498 if (seam[height - 1][0] > 1)
502 const ImagePixelType sum = 2 * target[height - 2][0] + target[height - 1][width - 1] + target[height - 1][1];
503 error[height - 1][0] = (4 * target[height - 1][0] - sum - gradient[height - 1][0]);
507 const ImagePixelType sum = 2 * target[height - 2][0] + 2 * target[height - 1][1];
508 error[height - 1][0] = (4 * target[height - 1][0] - sum - gradient[height - 1][0]);
511 for (
int x = 1; x < width - 1; ++x)
513 if (seam[height - 1][x]>1)
515 const ImagePixelType sum = 2 * target[height-2][x] +
detail::GetBorderValues(x, height - 1, 1, 0, target, seam);
516 error[height - 1][x] = (4 * target[height - 1][x] - sum - gradient[height - 1][x]);
519 if (seam[height - 1][width - 1] > 1)
523 const ImagePixelType sum = 2 * target[height - 2][width - 1] + target[height - 1][width - 2] + target[height - 1][0];
524 error[height - 1][width - 1] = (4 * target[height - 1][width - 1] - sum - gradient[height - 1][width - 1]);
528 const ImagePixelType sum = 2 * target[height - 2][width - 1] + 2 * target[height - 1][width - 2];
529 error[height - 1][width - 1] = (4 * target[height - 1][width - 1] - sum - gradient[height - 1][width - 1]);
536 template <
class PixelType>
542 template <
class ITERATOR>
550 return vigra::NumericTraits<PixelType>::zero();
554 template <
class ITERATOR,
class DIFFERENCE>
557 if (PixelType(i[d]) >=
m_value)
563 return vigra::NumericTraits<PixelType>::zero();
569 template <
class PixelType>
575 template <
class ITERATOR>
583 return vigra::NumericTraits<PixelType>::zero();
587 template <
class ITERATOR,
class DIFFERENCE>
596 return vigra::NumericTraits<PixelType>::zero();
602 template <
class Image,
class Pyram
idImage>
603 void BuildSeamPyramid(
const Image& input, vigra::ImagePyramid<PyramidImage>& seams,
const int minLength)
605 const int nlevels = (int)ceil(
log(
std::min(input.width(), input.height()) / (
double)minLength) /
log(2.0));
606 seams.resize(0, nlevels, input.size());
607 Image scaledImage = input;
609 for (
size_t i = 1; i <= seams.highestLevel(); ++i)
611 Image smaller((scaledImage.width() + 1) / 2, (scaledImage.height() + 1) / 2);
614 scaledImage = smaller;
618 template<
class Image,
class Mask,
class SeamMask,
class GradientType>
619 void BuildGradientMap(
const Image& image1,
const Image& image2,
const Mask& mask2,
const SeamMask& seam, GradientType& gradient,
const vigra::Point2D& offset,
const bool doWrap)
621 typedef typename GradientType::PixelType GradientPixelType;
622 const int width = image2.width();
623 const int height = image2.height();
629 GradientPixelType value = 4 * image2[0][0] - image2[0][1] - 2 * image2[1][0] - image2[0][width - 1];
634 gradient[0][0] = value;
638 GradientPixelType value = 4 * image2[0][0] - 2 * image2[0][1] - 2 * image2[1][0];
642 gradient[0][0] = value;
645 for (
int x = 1; x < width - 1; ++x)
653 gradient[0][x] = value;
656 if (seam[0][width - 1] == 2)
660 GradientPixelType value = 4 * image2[0][width - 1] - image2[0][width - 2] - 2 * image2[1][width - 1] - image2[0][0];
664 gradient[0][width - 1] = value;
668 GradientPixelType value = 4 * image2[0][width - 1] - 2 * image2[0][width - 2] - 2 * image2[1][width - 1];
671 gradient[0][width - 1] = value;
674 #pragma omp parallel for
675 for (
int y = 1; y < height - 1; ++y)
681 GradientPixelType value = 4 * image2[y][0] -
detail::ProcessBorderPixel(0, y, 0, 1, image2, mask2, seam) - image2[y][1] - image2[y][width - 1];
686 gradient[y][0] = value;
694 gradient[y][0] = value;
697 for (
int x = 1; x < width - 1; ++x)
699 const typename SeamMask::PixelType seamVal = seam[y][x];
702 GradientPixelType value = 4.0 * image2[y][x];
719 gradient[y][x] = value;
722 if (seam[y][width - 1] == 2)
726 GradientPixelType value = 4.0 * image2[y][width - 1] -
detail::ProcessBorderPixel(width - 1, y, 0, 1, image2, mask2, seam) - image2[y][width - 2] - image2[y][0];
731 gradient[y][width - 1] = value;
735 GradientPixelType value = 4.0 * image2[y][width - 1] -
detail::ProcessBorderPixel(width - 1, y, 0, 1, image2, mask2, seam) - 2 * image2[y][width - 2];
739 gradient[y][width - 1] = value;
744 if (seam[height - 1][0] == 2)
748 GradientPixelType value = 4 * image2[height - 1][0] - image2[height - 1][1] - 2 * image2[height - 2][0] - image2[height - 1][width - 1];
752 gradient[height - 1][0] = value;
756 GradientPixelType value = 4 * image2[height - 1][0] - 2 * image2[height - 1][1] - 2 * image2[height - 2][0];
759 gradient[height - 1][0] = value;
762 for (
size_t x = 1; x < width - 1; ++x)
764 if (seam[height - 1][x] == 2)
766 GradientPixelType value = 4 * image2[height - 1][x] -
detail::ProcessBorderPixel(x, height - 1, 1, 0, image2, mask2, seam) - 2 * image2[height - 2][x];
770 gradient[height - 1][x] = value;
773 if (seam[height - 1][width - 1] == 2)
777 GradientPixelType value = 4 * image2[height - 1][width - 1] - image2[height - 1][width - 2] - 2 * image2[height - 2][width - 1] - image2[height - 1][0];
781 gradient[height - 1][width - 1] = value;
785 GradientPixelType value = 4 * image2[height - 1][width - 1] - 2 * image2[height - 1][width - 2] - 2 * image2[height - 2][width - 1];
788 gradient[height - 1][width - 1] = value;
793 template <
class Image,
class SeamMask>
794 void Multigrid(Image& out,
const Image& gradient,
const vigra::ImagePyramid<SeamMask>& seamMaskPyramid,
int minLen,
const float errorThreshold,
const int maxIter,
const bool doWrap)
796 const int width = out.width();
797 const int height = out.height();
799 if (width < minLen || height < minLen)
803 Image err(width, height);
804 Image err2((width + 1) / 2, (height + 1) / 2);
805 Image out2(err2.size());
807 for (
int i = 0; i <= seamMaskPyramid.highestLevel(); ++i)
809 if (out.size() == seamMaskPyramid[i].size())
817 std::cout <<
"ERROR: No suitable mask, this should not happen." << std::endl
818 <<
"searching " << out.size() <<
", finest " << seamMaskPyramid[seamMaskPyramid.highestLevel()].size() << std::endl;
822 const float omega = 1.6f;
823 detail::SOR(out, gradient, seamMaskPyramid[maskIndex], omega, errorThreshold, maxIter, doWrap);
826 Multigrid(out2, err2, seamMaskPyramid, minLen, errorThreshold, maxIter, doWrap);
828 Multigrid(out2, err2, seamMaskPyramid, minLen, errorThreshold, maxIter, doWrap);
833 vigra::functor::Arg1() - vigra::functor::Arg2());
835 detail::SOR(out, gradient, seamMaskPyramid[maskIndex], omega, errorThreshold, maxIter, doWrap);
842 #endif //POISSON_BLEND_H
void RestrictErrorToNextLevel(const Image &in, Image &out)
PixelType operator()(ITERATOR const &i) const
Image::PixelType GetBorderGradient(const int x, const int y, const int dx, const int dy, const SeamMask &seams, const Image &image1, const vigra::Point2D &offset)
double GetRealValue(const ComponentType &val)
MaskGreaterAccessor(PixelType val)
PixelType operator()(ITERATOR const &i, DIFFERENCE d) const
vigra::NumericTraits< typename Image::PixelType >::RealPromote GetBorderValues(const int x, const int y, int dx, int dy, const Image &image, const SeamMask &seams)
vigra::pair< typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::ImageConstAccessor > srcImage(const ROIImage< Image, Mask > &img)
void CalcResidualError(Image &error, const Image &target, const Image &gradient, const SeamMask &seam, const bool doWrap)
void BuildGradientMap(const Image &image1, const Image &image2, const Mask &mask2, const SeamMask &seam, GradientType &gradient, const vigra::Point2D &offset, const bool doWrap)
PixelType operator()(ITERATOR const &i) const
void SOR(Image &target, const Image &gradient, const SeamMask &seams, const float omega, const float errorThreshold, const int maxIter, const bool doWrap)
vigra::Int8Image FindEdgesForPoisson(const Image &input)
mark edges in input image for poisson blending * input: expected an image with following meanings lab...
void combineTwoImagesIf(SrcImageIterator1 src1_upperleft, SrcImageIterator1 src1_lowerright, SrcAccessor1 src1_acc, SrcImageIterator2 src2_upperleft, SrcAccessor2 src2_acc, MaskImageIterator mask_upperleft, MaskAccessor mask_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc, const Functor &func)
MaskSmallerAccessor(PixelType val)
vigra::NumericTraits< typename Image::PixelType >::RealPromote ProcessNeighborPixels(const int x, const int y, const int dx, const int dy, const Image &image, const Mask &mask)
vigra::pair< typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::ImageAccessor > destImage(ROIImage< Image, Alpha > &img)
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
vigra::RGBValue< T, RIDX, GIDX, BIDX > log(vigra::RGBValue< T, RIDX, GIDX, BIDX > const &v)
component-wise logarithm
PixelType operator()(ITERATOR const &i, DIFFERENCE d) const
vigra::Int8 operator()(float const &vf) const
void SimpleConvolveImage4(const Image1 &image1, Image2 &image2, const double factor1, const double factor2)
vigra::triple< typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::ImageAccessor > destImageRange(ROIImage< Image, Alpha > &img)
void transformImage(SrcImageIterator src_upperleft, SrcImageIterator src_lowerright, SrcAccessor src_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc, const Functor &func)
void BuildSeamPyramid(const Image &input, vigra::ImagePyramid< PyramidImage > &seams, const int minLength)
vigra::NumericTraits< typename Image::PixelType >::RealPromote ProcessBorderPixel(const int x, const int y, const int dx, const int dy, const Image &image, const Mask &mask, const SeamMask &seam)
void Multigrid(Image &out, const Image &gradient, const vigra::ImagePyramid< SeamMask > &seamMaskPyramid, int minLen, const float errorThreshold, const int maxIter, const bool doWrap)