28 #include <vigra/seededregiongrowing.hxx>
29 #include <vigra/convolution.hxx>
43 template <
class PixelType>
44 PixelType
operator()(PixelType
const& v1, PixelType
const& v2)
const
46 return (v1 & 1) | (v2 & 2);
58 template <
class PixelType>
59 double operator()(PixelType
const& v1, PixelType
const& v2)
const
61 return std::abs(static_cast<double>(v1 - v2));
63 template <
class PixelType>
64 double operator()(vigra::RGBValue<PixelType>
const& v1, vigra::RGBValue<PixelType>
const& v2)
const
66 return sqrt(
square(v1.red() - v2.red()) +
square(v1.green() - v2.green()) +
square(v1.blue() - v2.blue()));
72 template <
class PixelType>
73 PixelType
operator()(PixelType
const& v1, PixelType
const& v2)
const
81 return vigra::NumericTraits<PixelType>::zero();
88 template <
class PixelType>
89 PixelType
operator()(PixelType
const& v1, PixelType
const& v2)
const
103 return vigra::NumericTraits<PixelType>::zero();
109 template <
class ImageType>
120 template <
class ImageType,
class MaskType>
121 void PoissonBlend(
ImageType& image1,
const ImageType& image2,
const MaskType& mask2,
const vigra::BImage& labels,
const vigra::Point2D& offsetPoint,
const bool doWrap)
124 vigra::ImagePyramid<vigra::Int8Image> seams;
125 const int minLength = 8;
128 typedef typename vigra::NumericTraits<typename ImageType::PixelType>::RealPromote ImageRealPixelType;
129 vigra::BasicImage<ImageRealPixelType> gradient(image2.size());
130 vigra::BasicImage<ImageRealPixelType> target(image2.size());
141 template <
class ImageType,
class MaskType>
142 void MergeImages(
ImageType& image1, MaskType& mask1,
const ImageType& image2,
const MaskType& mask2,
const vigra::Diff2D offset,
const bool wrap,
const bool hardSeam)
144 const vigra::Point2D offsetPoint(offset);
145 const vigra::Rect2D offsetRect(offsetPoint, mask2.size());
147 if (image1.width() < offsetRect.lowerRight().x || image1.height() < offsetRect.lowerRight().y)
153 vigra::BImage labels(image2.size());
161 vigra::ArrayOfRegionStatistics<vigra::FindBoundingRectangle> roi(3);
162 vigra::inspectTwoImages(vigra::srcIterRange<vigra::Diff2D>(vigra::Diff2D(0, 0), labels.size()),
vigra::srcImage(labels), roi);
164 const bool haveOverlap = roi.regions[3].size().area() > 0;
165 if (!haveOverlap && hardSeam)
173 if (roi.regions[2].size().area() == 0)
179 if (roi.regions[1].size().area() == 0)
188 const double smoothRadius =
std::max(1.0,
std::max(roi.regions[3].size().width(), roi.regions[3].size().height()) / 1000.0);
195 vigra::Point2D p1(roi.regions[3].upperLeft);
204 vigra::Point2D p2(roi.regions[3].lowerRight);
205 if (p2.x + 1 < image2.width())
209 if (p2.y + 1 < image2.height())
213 vigra::DImage diff(p2 - p1);
214 const vigra::Rect2D rect1(offsetPoint + p1, diff.size());
218 vigra::FindMinMax<double> diffMinMax;
220 diffMinMax.max = std::min<double>(diffMinMax.max, 0.25f * vigra::NumericTraits<typename vigra::NumericTraits<typename ImageType::PixelType>::ValueType>
::max());
221 vigra::BImage diffByte(diff.size());
225 vigra::ArrayOfRegionStatistics<vigra::SeedRgDirectValueFunctor<vigra::UInt8> > stats(3);
226 if (wrap && (roi.regions[3].size().width() == image1.width()))
230 const int oldWidth = labels.width();
231 const int oldHeight = labels.height();
232 vigra::BImage labelsWrapped(oldWidth * 2, oldHeight);
234 vigra::omp::copyImage(labels.upperLeft(), labels.lowerRight(), labels.accessor(), labelsWrapped.upperLeft() + vigra::Diff2D(oldWidth, 0), labelsWrapped.accessor());
235 vigra::BImage diffWrapped(oldWidth * 2, diffByte.height());
237 vigra::omp::copyImage(diffByte.upperLeft(), diffByte.lowerRight(), diffByte.accessor(), diffWrapped.upperLeft() + vigra::Diff2D(oldWidth, 0), diffWrapped.accessor());
240 if (diffWrapped.width() > 3 * smoothRadius && diffWrapped.height() > 3 * smoothRadius)
245 vigra::omp::copyImage(labelsWrapped.upperLeft() + vigra::Diff2D(oldWidth / 2, 0), labelsWrapped.upperLeft() + vigra::Diff2D(oldWidth, oldHeight), labelsWrapped.accessor(),
246 labels.upperLeft() + vigra::Diff2D(oldWidth / 2, 0), labels.accessor());
247 vigra::omp::copyImage(labelsWrapped.upperLeft() + vigra::Diff2D(oldWidth, 0), labelsWrapped.upperLeft() + vigra::Diff2D(oldWidth + oldWidth / 2, oldHeight), labelsWrapped.accessor(),
248 labels.upperLeft(), labels.accessor());
254 if (diffByte.width() > 3 * smoothRadius && diffByte.height() > 3 * smoothRadius)
276 const bool doWrapBlending = wrap && (
278 (roi.regions[3].size().width() == image1.width()) ||
280 (roi.regions[2].size().width() == image1.width())
286 PoissonBlend(image1, image2, mask2, labels, offsetPoint, doWrapBlending);
PixelType operator()(PixelType const &v1, PixelType const &v2) const
void copyImageIf(SrcImageIterator src_upperleft, SrcImageIterator src_lowerright, SrcAccessor src_acc, MaskImageIterator mask_upperleft, MaskAccessor mask_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc)
solve poisson equation for blending images
double operator()(PixelType const &v1, PixelType const &v2) const
PixelType operator()(PixelType const &v1, PixelType const &v2) const
void MergeImages(ImageType &image1, MaskType &mask1, const ImageType &image2, const MaskType &mask2, const vigra::Diff2D offset, const bool wrap, const bool hardSeam)
vigra::pair< typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::ImageConstAccessor > srcImage(const ROIImage< Image, Mask > &img)
double operator()(vigra::RGBValue< PixelType > const &v1, vigra::RGBValue< PixelType > const &v2) const
void combineTwoImages(SrcImageIterator1 src1_upperleft, SrcImageIterator1 src1_lowerright, SrcAccessor1 src1_acc, SrcImageIterator2 src2_upperleft, SrcAccessor2 src2_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc, const Functor &func)
void BuildGradientMap(const Image &image1, const Image &image2, const Mask &mask2, const SeamMask &seam, GradientType &gradient, const vigra::Point2D &offset, const bool doWrap)
vigra::FRGBImage ImageType
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
ImageType ResizeImage(const ImageType &image, const vigra::Size2D &newSize)
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 copyImage(SrcImageIterator src_upperleft, SrcImageIterator src_lowerright, SrcAccessor src_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc)
PixelType operator()(PixelType const &v1, PixelType const &v2) const
void BuildSeamPyramid(const Image &input, vigra::ImagePyramid< PyramidImage > &seams, const int minLength)
void PoissonBlend(ImageType &image1, const ImageType &image2, const MaskType &mask2, const vigra::BImage &labels, const vigra::Point2D &offsetPoint, const bool doWrap)
void Multigrid(Image &out, const Image &gradient, const vigra::ImagePyramid< SeamMask > &seamMaskPyramid, int minLen, const float errorThreshold, const int maxIter, const bool doWrap)