Hugin trunk 0.1
Loading...
Searching...
No Matches
BlendPoisson.h
Go to the documentation of this file.
1// -*- c-basic-offset: 4 -*-
2
12/* This program is free software; you can redistribute it and/or
13* modify it under the terms of the GNU General Public
14* License as published by the Free Software Foundation; either
15* version 2 of the License, or (at your option) any later version.
16*
17* This software is distributed in the hope that it will be useful,
18* but WITHOUT ANY WARRANTY; without even the implied warranty of
19* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20* General Public License for more details.
21*
22* You should have received a copy of the GNU General Public
23* License along with this software. If not, see
24* <http://www.gnu.org/licenses/>.
25*
26*/
27
28#ifndef POISSON_BLEND_H
29#define POISSON_BLEND_H
30
31#include <iostream>
32#include <vigra/stdimage.hxx>
33#include <vigra/convolution.hxx>
34#include <vigra/stdconvolution.hxx>
35#include <vigra/basicgeometry.hxx>
36#include "openmp_vigra.h"
37
38#define DEBUG_TIMING
39
40namespace vigra_ext
41{
42
43namespace poisson
44{
45
46namespace detail
47{
48// helper functions for build gradient map
49template <class Image, class Mask>
50inline 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)
51{
52 const typename Mask::PixelType m1 = mask[y + dy][x + dx];
53 const typename Mask::PixelType m2 = mask[y - dy][x - dx];
54 if (m1 > 0 && m2 > 0)
55 {
56 return image[y + dy][x + dx] + image[y - dy][x - dx];
57 };
58 // if one of the 2 pixel has no information (outside of image area)
59 // we extrapolate the value from neighbor pixel
60 if (m1 > 0)
61 {
62 return 2 * image[y + dy][x + dx];
63 }
64 else
65 {
66 return 2 * image[y - dy][x - dx];
67 };
68};
69
70template <class Image, class Mask, class SeamMask>
71inline 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)
72{
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)
78 {
79 if (mask1 > 0 && mask2 > 0)
80 {
81 return image[y + dy][x + dx] + image[y - dy][x - dx];
82 };
83 if (mask1 > 0)
84 {
85 return 2 *image[y + dy][x + dx];
86 }
87 else
88 {
89 return 2 * image[y - dy][x - dx];
90 };
91 };
92 if (seam1 > 0)
93 {
94 if (mask1 > 0)
95 {
96 return 2 * image[y + dy][x + dx];
97 }
98 else
99 {
100 return vigra::NumericTraits<typename vigra::NumericTraits<typename Image::PixelType>::RealPromote>::zero();
101 };
102 };
103 if (seam2 > 0)
104 {
105 if (mask2 > 0)
106 {
107 return 2 * image[y - dy][x - dx];
108 }
109 else
110 {
111 return vigra::NumericTraits<typename vigra::NumericTraits<typename Image::PixelType>::RealPromote>::zero();
112 };
113 };
114 return vigra::NumericTraits<typename vigra::NumericTraits<typename Image::PixelType>::RealPromote>::zero();
115};
116
117template <class Image, class SeamMask>
118inline 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)
119{
120 if (seams[y + dy][x + dx] == 1)
121 {
122 return image1[offset.y + y + dy][offset.x + x + dx];
123 };
124 return vigra::NumericTraits<typename Image::PixelType>::zero();
125};
126
127template <class Image, class SeamMask>
128inline typename vigra::NumericTraits<typename Image::PixelType>::RealPromote GetBorderValues(const int x, const int y, int dx, int dy, const Image& image, const SeamMask& seams)
129{
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)
133 {
134 return image[y + dy][x + dx] + image[y - dy][x - dx];
135 }
136 else
137 {
138 return (2 - std::min<int>(s2, 2))*image[y + dy][x + dx] + (2 - std::min<int>(s1, 2))*image[y - dy][x - dx];
139 };
140};
141
142template <class Image>
143void RestrictErrorToNextLevel(const Image & in, Image & out)
144{
145 // smooth input 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());
149 vigra::convolveImage(vigra::srcImageRange(in), vigra::destImage(smoothImage), vigra::kernel2d(filter2D));
150 // downsample smoothed image
151 vigra::resizeImageLinearInterpolation(vigra::srcImageRange(smoothImage), vigra::destImageRange(out));
152}
153
154// internal use by FindEdgesForPoisson
156{
157 vigra::Int8 operator()(float const& vf) const
158 {
159 const int v = static_cast<int>(vf);
160 if (v > 0 && v < 17)
161 {
162 return 1;
163 }
164 else
165 {
166 if (v >= 85)
167 {
168 if (v == 85 || v == 89 || v == 93 || v == 97)
169 {
170 return 3;
171 }
172 else
173 {
174 return 2;
175 }
176 }
177 else
178 {
179 return 0;
180 };
181 };
182 };
183};
184
185// multi-threaded variant for convolution of images, only 4-neighborhood is considerd
186template <class Image1, class Image2>
187void SimpleConvolveImage4(const Image1& image1, Image2& image2, const double factor1, const double factor2)
188{
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();
193 //special treatment of first line
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)
196 {
197 image2[0][x] = factor1*image1[0][x] + factor2*image1[0][x - 1] + factor2*image1[0][x + 1] + factor2*image1[1][x];
198 };
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)
202 {
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)
206 {
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];
209 }
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];
212 };
213 //special treatment of last line
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)
216 {
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];
218 };
219 image2[height - 1][width - 1] = factor1*image1[height - 1][width - 1] + factor2*image1[height - 1][width - 2] + factor2*image1[height - 2][width - 1];
220};
221
234template <class Image>
235vigra::Int8Image FindEdgesForPoisson(const Image& input)
236{
237 vigra::Int8Image output(input.size());
240 return output;
241};
242
243template <class ComponentType>
244double GetRealValue(const ComponentType& val) { return val; }
245
246template <class ComponentType>
247double GetRealValue(const vigra::RGBValue<ComponentType>& val) { return val.magnitude(); }
248
249template <class Image, class SeamMask>
250void SOR(Image& target, const Image& gradient, const SeamMask& seams, const float omega, const float errorThreshold, const int maxIter, const bool doWrap)
251{
252 typedef typename Image::PixelType TargetPixelType;
253 const int width = target.width();
254 const int height = target.height();
255
256 // changes in last iteration
257 double oldError = 0;
258 for (int j = 0; j < maxIter; j++)
259 {
260 // changes in current iteration
261 double error = 0;
262 // special treatment of first line
263 if (seams[0][0] > 1)
264 {
265 if (doWrap)
266 {
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;
270 }
271 else
272 {
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;
276 };
277 };
278 for (int x = 1; x < width - 1; ++x)
279 {
280 if (seams[0][x] > 1)
281 {
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;
285 };
286 };
287 if (seams[0][width - 1] > 1)
288 {
289 if (doWrap)
290 {
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;
294 }
295 else
296 {
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;
300 };
301 };
302#pragma omp parallel for reduction(+: error) schedule(dynamic, 100)
303 for (int y = 1; y < height - 1; ++y)
304 {
305 if (seams[y][0] > 1)
306 {
307 if (doWrap)
308 {
309 const TargetPixelType delta = omega * ((gradient[y][0] + detail::GetBorderValues(0, y, 0, 1, target, seams)
310 + target[y][1] + target[y][width - 1]) / 4.0f - target[y][0]);
312 target[y][0] += delta;
313 }
314 else
315 {
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;
319 };
320 };
321 for (int x = 1; x < width - 1; ++x)
322 {
323 const typename SeamMask::value_type maskValue = seams[y][x];
324 if (maskValue > 1)
325 {
326 if (maskValue == 2)
327 {
328 // border pixel
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;
333 }
334 else
335 {
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;
340 };
341 };
342 };
343 if (seams[y][width - 1] > 1)
344 {
345 if (doWrap)
346 {
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;
350 }
351 else
352 {
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;
356 };
357 };
358 };
359 // last line
360 if (seams[height - 1][0] > 1)
361 {
362 if (doWrap)
363 {
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;
367 }
368 else
369 {
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;
373 };
374 };
375 for (int x = 1; x < width - 1; ++x)
376 {
377 if (seams[height - 1][x] > 1)
378 {
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;
382 };
383 };
384 if (seams[height - 1][width - 1] > 1)
385 {
386 if (doWrap)
387 {
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;
391 }
392 else
393 {
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;
397 };
398 };
399
400 if (oldError > 0 && oldError / error < std::pow(10, errorThreshold))
401 {
402 break;
403 }
404 oldError = error;
405 }
406}
407
408template <class Image, class SeamMask>
409void CalcResidualError(Image& error, const Image& target, const Image& gradient, const SeamMask& seam, const bool doWrap)
410{
411 typedef typename Image::PixelType ImagePixelType;
412 const int width = target.width();
413 const int height = target.height();
414
415 if (seam[0][0] > 1)
416 {
417 if (doWrap)
418 {
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]);
421 }
422 else
423 {
424 const ImagePixelType sum = 2 * target[1][0] + 2 * target[0][1];
425 error[0][0] = (4 * target[0][0] - sum - gradient[0][0]);
426 };
427 };
428 for (int x = 1; x < width - 1; ++x)
429 {
430 if (seam[0][x]>1)
431 {
432 const ImagePixelType sum = 2 * target[1][x] + detail::GetBorderValues(x, 0, 1, 0, target, seam);
433 error[0][x] = (4 * target[0][x] - sum - gradient[0][x]);
434 };
435 };
436 if (seam[0][width - 1] > 1)
437 {
438 if (doWrap)
439 {
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]);
442 }
443 else
444 {
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]);
447 }
448 };
449#pragma omp parallel for schedule(dynamic, 100)
450 for (int y = 1; y < height - 1; ++y)
451 {
452 if (seam[y][0] > 1)
453 {
454 if (doWrap)
455 {
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]);
458 }
459 else
460 {
461 const ImagePixelType sum = detail::GetBorderValues(0, y, 0, 1, target, seam) + 2 * target[y][1];
462 error[y][0] = (4 * target[y][0] - sum - gradient[y][0]);
463 };
464 }
465 for (int x = 1; x < width - 1; ++x)
466 {
467 const typename SeamMask::value_type maskValue = seam[y][x];
468 if (maskValue > 1)
469 {
470 if (maskValue == 2)
471 {
472 // border pixel
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]);
475 }
476 else
477 {
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]);
480 };
481 };
482 };
483 if (seam[y][width - 1] > 1)
484 {
485 if (doWrap)
486 {
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]);
489 }
490 else
491 {
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]);
494 };
495 };
496 };
497 // last line
498 if (seam[height - 1][0] > 1)
499 {
500 if (doWrap)
501 {
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]);
504 }
505 else
506 {
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]);
509 };
510 };
511 for (int x = 1; x < width - 1; ++x)
512 {
513 if (seam[height - 1][x]>1)
514 {
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]);
517 };
518 };
519 if (seam[height - 1][width - 1] > 1)
520 {
521 if (doWrap)
522 {
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]);
525 }
526 else
527 {
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]);
530 };
531 };
532
533}
534
535} // namespace detail
536template <class PixelType>
538{
539public:
540 explicit MaskGreaterAccessor(PixelType val) : m_value(val) {};
541
542 template <class ITERATOR>
543 PixelType operator()(ITERATOR const & i) const {
544 if (PixelType(*i) >= m_value)
545 {
546 return vigra::NumericTraits<PixelType>::max();
547 }
548 else
549 {
550 return vigra::NumericTraits<PixelType>::zero();
551 };
552 }
553
554 template <class ITERATOR, class DIFFERENCE>
555 PixelType operator()(ITERATOR const & i, DIFFERENCE d) const
556 {
557 if (PixelType(i[d]) >= m_value)
558 {
559 return vigra::NumericTraits<PixelType>::max();
560 }
561 else
562 {
563 return vigra::NumericTraits<PixelType>::zero();
564 };
565 }
566 PixelType m_value;
567};
568
569template <class PixelType>
571{
572public:
573 explicit MaskSmallerAccessor(PixelType val) : m_value(val) {};
574
575 template <class ITERATOR>
576 PixelType operator()(ITERATOR const & i) const {
577 if (PixelType(*i) < m_value)
578 {
579 return vigra::NumericTraits<PixelType>::max();
580 }
581 else
582 {
583 return vigra::NumericTraits<PixelType>::zero();
584 };
585 }
586
587 template <class ITERATOR, class DIFFERENCE>
588 PixelType operator()(ITERATOR const & i, DIFFERENCE d) const
589 {
590 if (PixelType(i[d]) < m_value)
591 {
592 return vigra::NumericTraits<PixelType>::max();
593 }
594 else
595 {
596 return vigra::NumericTraits<PixelType>::zero();
597 };
598 }
599 PixelType m_value;
600};
601
602template <class Image, class PyramidImage>
603void BuildSeamPyramid(const Image& input, vigra::ImagePyramid<PyramidImage>& seams, const int minLength)
604{
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)
610 {
611 Image smaller((scaledImage.width() + 1) / 2, (scaledImage.height() + 1) / 2);
612 vigra::resizeImageNoInterpolation(vigra::srcImageRange(scaledImage), vigra::destImageRange(smaller));
615 };
616}
617
618template<class Image, class Mask, class SeamMask, class GradientType>
619void BuildGradientMap(const Image& image1, const Image& image2, const Mask& mask2, const SeamMask& seam, GradientType& gradient, const vigra::Point2D& offset, const bool doWrap)
620{
621 typedef typename GradientType::PixelType GradientPixelType;
622 const int width = image2.width();
623 const int height = image2.height();
624 //special treatment of first line
625 if (seam[0][0] == 2)
626 {
627 if (doWrap)
628 {
629 GradientPixelType value = 4 * image2[0][0] - image2[0][1] - 2 * image2[1][0] - image2[0][width - 1];
630 // copy values for Dirichlet boundary condition
631 value += detail::GetBorderGradient(0, 0, 1, 0, seam, image1, offset);
632 value += detail::GetBorderGradient(0, 0, 0, 1, seam, image1, offset);
633 value += detail::GetBorderGradient(width - 2, 0, 1, 0, seam, image1, offset);
634 gradient[0][0] = value;
635 }
636 else
637 {
638 GradientPixelType value = 4 * image2[0][0] - 2 * image2[0][1] - 2 * image2[1][0];
639 // copy values for Dirichlet boundary condition
640 value += detail::GetBorderGradient(0, 0, 1, 0, seam, image1, offset);
641 value += detail::GetBorderGradient(0, 0, 0, 1, seam, image1, offset);
642 gradient[0][0] = value;
643 };
644 };
645 for (int x = 1; x < width - 1; ++x)
646 {
647 if (seam[0][x] == 2)
648 {
649 GradientPixelType value = 4 * image2[0][x] - detail::ProcessBorderPixel(x, 0, 1, 0, image2, mask2, seam) - 2 * image2[1][x];
650 value += detail::GetBorderGradient(x, 0, -1, 0, seam, image1, offset);
651 value += detail::GetBorderGradient(x, 0, 1, 0, seam, image1, offset);
652 value += detail::GetBorderGradient(x, 0, 0, 1, seam, image1, offset);
653 gradient[0][x] = value;
654 };
655 };
656 if (seam[0][width - 1] == 2)
657 {
658 if (doWrap)
659 {
660 GradientPixelType value = 4 * image2[0][width - 1] - image2[0][width - 2] - 2 * image2[1][width - 1] - image2[0][0];
661 value += detail::GetBorderGradient(width - 1, 0, -1, 0, seam, image1, offset);
662 value += detail::GetBorderGradient(width - 1, 0, 0, 1, seam, image1, offset);
663 value += detail::GetBorderGradient(1, 0, -1, 0, seam, image1, offset);
664 gradient[0][width - 1] = value;
665 }
666 else
667 {
668 GradientPixelType value = 4 * image2[0][width - 1] - 2 * image2[0][width - 2] - 2 * image2[1][width - 1];
669 value += detail::GetBorderGradient(width - 1, 0, -1, 0, seam, image1, offset);
670 value += detail::GetBorderGradient(width - 1, 0, 0, 1, seam, image1, offset);
671 gradient[0][width - 1] = value;
672 };
673 };
674#pragma omp parallel for
675 for (int y = 1; y < height - 1; ++y)
676 {
677 if (seam[y][0] == 2)
678 {
679 if (doWrap)
680 {
681 GradientPixelType value = 4 * image2[y][0] - detail::ProcessBorderPixel(0, y, 0, 1, image2, mask2, seam) - image2[y][1] - image2[y][width - 1];
682 value += detail::GetBorderGradient(0, y, 1, 0, seam, image1, offset);
683 value += detail::GetBorderGradient(0, y, 0, 1, seam, image1, offset);
684 value += detail::GetBorderGradient(0, y, 0, -1, seam, image1, offset);
685 value += detail::GetBorderGradient(width - 2, y, 1, 0, seam, image1, offset);
686 gradient[y][0] = value;
687 }
688 else
689 {
690 GradientPixelType value = 4 * image2[y][0] - detail::ProcessBorderPixel(0, y, 0, 1, image2, mask2, seam) - 2 * image2[y][1];
691 value += detail::GetBorderGradient(0, y, 1, 0, seam, image1, offset);
692 value += detail::GetBorderGradient(0, y, 0, 1, seam, image1, offset);
693 value += detail::GetBorderGradient(0, y, 0, -1, seam, image1, offset);
694 gradient[y][0] = value;
695 };
696 };
697 for (int x = 1; x < width - 1; ++x)
698 {
699 const typename SeamMask::PixelType seamVal = seam[y][x];
700 if (seamVal>1)
701 {
702 GradientPixelType value = 4.0 * image2[y][x];
703 if (seamVal == 3)
704 {
705 value -= detail::ProcessNeighborPixels(x, y, 1, 0, image2, mask2);
706 value -= detail::ProcessNeighborPixels(x, y, 0, 1, image2, mask2);
707 }
708 else
709 {
710 // seamVal==2, border pixel
711 value -= detail::ProcessBorderPixel(x, y, 1, 0, image2, mask2, seam);
712 value -= detail::ProcessBorderPixel(x, y, 0, 1, image2, mask2, seam);
713 };
714 // copy values for Dirichlet boundary condition
715 value += detail::GetBorderGradient(x, y, 1, 0, seam, image1, offset);
716 value += detail::GetBorderGradient(x, y, 0, 1, seam, image1, offset);
717 value += detail::GetBorderGradient(x, y, -1, 0, seam, image1, offset);
718 value += detail::GetBorderGradient(x, y, 0, -1, seam, image1, offset);
719 gradient[y][x] = value;
720 }
721 };
722 if (seam[y][width - 1] == 2)
723 {
724 if (doWrap)
725 {
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];
727 value += detail::GetBorderGradient(width - 1, y, -1, 0, seam, image1, offset);
728 value += detail::GetBorderGradient(width - 1, y, 0, 1, seam, image1, offset);
729 value += detail::GetBorderGradient(width - 1, y, 0, -1, seam, image1, offset);
730 value += detail::GetBorderGradient(0, y, -1, 0, seam, image1, offset);
731 gradient[y][width - 1] = value;
732 }
733 else
734 {
735 GradientPixelType value = 4.0 * image2[y][width - 1] - detail::ProcessBorderPixel(width - 1, y, 0, 1, image2, mask2, seam) - 2 * image2[y][width - 2];
736 value += detail::GetBorderGradient(width - 1, y, -1, 0, seam, image1, offset);
737 value += detail::GetBorderGradient(width - 1, y, 0, 1, seam, image1, offset);
738 value += detail::GetBorderGradient(width - 1, y, 0, -1, seam, image1, offset);
739 gradient[y][width - 1] = value;
740 };
741 };
742 };
743 //special treatment of last line
744 if (seam[height - 1][0] == 2)
745 {
746 if (doWrap)
747 {
748 GradientPixelType value = 4 * image2[height - 1][0] - image2[height - 1][1] - 2 * image2[height - 2][0] - image2[height - 1][width - 1];
749 value += detail::GetBorderGradient(0, height - 1, 1, 0, seam, image1, offset);
750 value += detail::GetBorderGradient(0, height - 1, 0, -1, seam, image1, offset);
751 value += detail::GetBorderGradient(width - 2, height - 1, 1, 0, seam, image1, offset);
752 gradient[height - 1][0] = value;
753 }
754 else
755 {
756 GradientPixelType value = 4 * image2[height - 1][0] - 2 * image2[height - 1][1] - 2 * image2[height - 2][0];
757 value += detail::GetBorderGradient(0, height - 1, 1, 0, seam, image1, offset);
758 value += detail::GetBorderGradient(0, height - 1, 0, -1, seam, image1, offset);
759 gradient[height - 1][0] = value;
760 };
761 };
762 for (size_t x = 1; x < width - 1; ++x)
763 {
764 if (seam[height - 1][x] == 2)
765 {
766 GradientPixelType value = 4 * image2[height - 1][x] - detail::ProcessBorderPixel(x, height - 1, 1, 0, image2, mask2, seam) - 2 * image2[height - 2][x];
767 value += detail::GetBorderGradient(x, height - 1, -1, 0, seam, image1, offset);
768 value += detail::GetBorderGradient(x, height - 1, 1, 0, seam, image1, offset);
769 value += detail::GetBorderGradient(x, height - 1, 0, -1, seam, image1, offset);
770 gradient[height - 1][x] = value;
771 };
772 };
773 if (seam[height - 1][width - 1] == 2)
774 {
775 if (doWrap)
776 {
777 GradientPixelType value = 4 * image2[height - 1][width - 1] - image2[height - 1][width - 2] - 2 * image2[height - 2][width - 1] - image2[height - 1][0];
778 value += detail::GetBorderGradient(width - 1, height - 1, -1, 0, seam, image1, offset);
779 value += detail::GetBorderGradient(width - 1, height - 1, 0, -1, seam, image1, offset);
780 value += detail::GetBorderGradient(1, height - 1, -1, 0, seam, image1, offset);
781 gradient[height - 1][width - 1] = value;
782 }
783 else
784 {
785 GradientPixelType value = 4 * image2[height - 1][width - 1] - 2 * image2[height - 1][width - 2] - 2 * image2[height - 2][width - 1];
786 value += detail::GetBorderGradient(width - 1, height - 1, -1, 0, seam, image1, offset);
787 value += detail::GetBorderGradient(width - 1, height - 1, 0, -1, seam, image1, offset);
788 gradient[height - 1][width - 1] = value;
789 };
790 };
791};
792
793template <class Image, class SeamMask>
794void Multigrid(Image& out, const Image& gradient, const vigra::ImagePyramid<SeamMask>& seamMaskPyramid, int minLen, const float errorThreshold, const int maxIter, const bool doWrap)
795{
796 const int width = out.width();
797 const int height = out.height();
798
799 if (width < minLen || height < minLen)
800 {
801 return;
802 }
803 Image err(width, height);
804 Image err2((width + 1) / 2, (height + 1) / 2);
805 Image out2(err2.size());
806 int maskIndex = -1;
807 for (int i = 0; i <= seamMaskPyramid.highestLevel(); ++i)
808 {
809 if (out.size() == seamMaskPyramid[i].size())
810 {
811 maskIndex = i;
812 break;
813 };
814 }
815 if (maskIndex == -1)
816 {
817 std::cout << "ERROR: No suitable mask, this should not happen." << std::endl
818 << "searching " << out.size() << ", finest " << seamMaskPyramid[seamMaskPyramid.highestLevel()].size() << std::endl;
819 return;
820 };
821 // pre-smoothing
822 const float omega = 1.6f; // relaxation parameter: 0 < omega < 2
827 // now do a W cycle calculation, another run of multigrid follows
829 vigra::resizeImageLinearInterpolation(srcImageRange(out2), destImageRange(err));
830 vigra::omp::combineTwoImagesIf(vigra::srcImageRange(out), vigra::srcImage(err),
832 vigra::destImage(out),
833 vigra::functor::Arg1() - vigra::functor::Arg2());
834 // post smoothing, using Gauss-Seidel, omega=1
836 return;
837}
838
839} // namespace poisson
840} // namespace vigra_ext
841
842#endif //POISSON_BLEND_H
PixelType operator()(ITERATOR const &i, DIFFERENCE d) const
PixelType operator()(ITERATOR const &i) const
PixelType operator()(ITERATOR const &i) const
PixelType operator()(ITERATOR const &i, DIFFERENCE d) const
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)
void transformImage(SrcImageIterator src_upperleft, SrcImageIterator src_lowerright, SrcAccessor src_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc, const Functor &func)
vigra::NumericTraits< typenameImage::PixelType >::RealPromote GetBorderValues(const int x, const int y, int dx, int dy, const Image &image, const SeamMask &seams)
vigra::Int8Image FindEdgesForPoisson(const Image &input)
mark edges in input image for poisson blending * input: expected an image with following meanings lab...
double GetRealValue(const ComponentType &val)
vigra::NumericTraits< typenameImage::PixelType >::RealPromote ProcessNeighborPixels(const int x, const int y, const int dx, const int dy, const Image &image, const Mask &mask)
void SimpleConvolveImage4(const Image1 &image1, Image2 &image2, const double factor1, const double factor2)
void CalcResidualError(Image &error, const Image &target, const Image &gradient, const SeamMask &seam, const bool doWrap)
void SOR(Image &target, const Image &gradient, const SeamMask &seams, const float omega, const float errorThreshold, const int maxIter, const bool doWrap)
void RestrictErrorToNextLevel(const Image &in, Image &out)
vigra::NumericTraits< typenameImage::PixelType >::RealPromote ProcessBorderPixel(const int x, const int y, const int dx, const int dy, const Image &image, const Mask &mask, const SeamMask &seam)
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)
void Multigrid(Image &out, const Image &gradient, const vigra::ImagePyramid< SeamMask > &seamMaskPyramid, int minLen, const float errorThreshold, const int maxIter, const bool doWrap)
void BuildSeamPyramid(const Image &input, vigra::ImagePyramid< PyramidImage > &seams, const int minLength)
void BuildGradientMap(const Image &image1, const Image &image2, const Mask &mask2, const SeamMask &seam, GradientType &gradient, const vigra::Point2D &offset, const bool doWrap)
vigra::pair< typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::ImageAccessor > destImage(ROIImage< Image, Alpha > &img)
Definition ROIImage.h:324
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::RGBValue< T, RIDX, GIDX, BIDX > log(vigra::RGBValue< T, RIDX, GIDX, BIDX > const &v)
component-wise logarithm
Definition utils.h:209
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
vigra::Int8 operator()(float const &vf) const
std::vector< deghosting::BImagePtr > threshold(const std::vector< deghosting::FImagePtr > &inputImages, const double threshold, const uint16_t flags)
Threshold function used for creating alpha masks for images.
Definition threshold.h:41