[ VIGRA Homepage | Function Index | Class Index | Namespaces | File List | Main Page ]

stdconvolution.hxx
1 /************************************************************************/
2 /* */
3 /* Copyright 1998-2002 by Ullrich Koethe */
4 /* */
5 /* This file is part of the VIGRA computer vision library. */
6 /* The VIGRA Website is */
7 /* http://hci.iwr.uni-heidelberg.de/vigra/ */
8 /* Please direct questions, bug reports, and contributions to */
9 /* ullrich.koethe@iwr.uni-heidelberg.de or */
10 /* vigra@informatik.uni-hamburg.de */
11 /* */
12 /* Permission is hereby granted, free of charge, to any person */
13 /* obtaining a copy of this software and associated documentation */
14 /* files (the "Software"), to deal in the Software without */
15 /* restriction, including without limitation the rights to use, */
16 /* copy, modify, merge, publish, distribute, sublicense, and/or */
17 /* sell copies of the Software, and to permit persons to whom the */
18 /* Software is furnished to do so, subject to the following */
19 /* conditions: */
20 /* */
21 /* The above copyright notice and this permission notice shall be */
22 /* included in all copies or substantial portions of the */
23 /* Software. */
24 /* */
25 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
26 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
27 /* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
28 /* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
29 /* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
30 /* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
31 /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
32 /* OTHER DEALINGS IN THE SOFTWARE. */
33 /* */
34 /************************************************************************/
35 
36 
37 #ifndef VIGRA_STDCONVOLUTION_HXX
38 #define VIGRA_STDCONVOLUTION_HXX
39 
40 #include <cmath>
41 #include "stdimage.hxx"
42 #include "bordertreatment.hxx"
43 #include "separableconvolution.hxx"
44 #include "utilities.hxx"
45 #include "sized_int.hxx"
46 
47 namespace vigra {
48 
49 /** \addtogroup StandardConvolution Two-dimensional convolution functions
50 
51 Perform 2D non-separable convolution, with and without ROI mask.
52 
53 These generic convolution functions implement
54 the standard 2D convolution operation for images that fit
55 into the required interface. Arbitrary ROI's are supported
56 by the mask version of the algorithm.
57 The functions need a suitable 2D kernel to operate.
58 */
59 //@{
60 
61 /** \brief Performs a 2 dimensional convolution of the source image using the given
62  kernel.
63 
64  The KernelIterator must point to the center of the kernel, and
65  the kernel's size is given by its upper left (x and y of distance <= 0) and
66  lower right (distance >= 0) corners. The image must always be larger than the
67  kernel. At those positions where the kernel does not completely fit
68  into the image, the specified \ref BorderTreatmentMode is
69  applied. You can choice between following BorderTreatmentModes:
70  <ul>
71  <li>BORDER_TREATMENT_CLIP</li>
72  <li>BORDER_TREATMENT_AVOID</li>
73  <li>BORDER_TREATMENT_WRAP</li>
74  <li>BORDER_TREATMENT_REFLECT</li>
75  <li>BORDER_TREATMENT_REPEAT</li>
76  </ul><br>
77  The images's pixel type (SrcAccessor::value_type) must be a
78  linear space over the kernel's value_type (KernelAccessor::value_type),
79  i.e. addition of source values, multiplication with kernel values,
80  and NumericTraits must be defined.
81  The kernel's value_type must be an algebraic field,
82  i.e. the arithmetic operations (+, -, *, /) and NumericTraits must
83  be defined.
84 
85  <b> Declarations:</b>
86 
87  pass arguments explicitly:
88  \code
89  namespace vigra {
90  template <class SrcIterator, class SrcAccessor,
91  class DestIterator, class DestAccessor,
92  class KernelIterator, class KernelAccessor>
93  void convolveImage(SrcIterator src_ul, SrcIterator src_lr, SrcAccessor src_acc,
94  DestIterator dest_ul, DestAccessor dest_acc,
95  KernelIterator ki, KernelAccessor ak,
96  Diff2D kul, Diff2D klr, BorderTreatmentMode border);
97  }
98  \endcode
99 
100 
101  use argument objects in conjunction with \ref ArgumentObjectFactories :
102  \code
103  namespace vigra {
104  template <class SrcIterator, class SrcAccessor,
105  class DestIterator, class DestAccessor,
106  class KernelIterator, class KernelAccessor>
107  void convolveImage(triple<SrcIterator, SrcIterator, SrcAccessor> src,
108  pair<DestIterator, DestAccessor> dest,
109  tuple5<KernelIterator, KernelAccessor, Diff2D, Diff2D,
110  BorderTreatmentMode> kernel);
111  }
112  \endcode
113 
114  <b> Usage:</b>
115 
116  <b>\#include</b> <vigra/stdconvolution.hxx><br>
117  Namespace: vigra
118 
119 
120  \code
121  vigra::FImage src(w,h), dest(w,h);
122  ...
123 
124  // define horizontal Sobel filter
125  vigra::Kernel2D<float> sobel;
126 
127  sobel.initExplicitly(Diff2D(-1,-1), Diff2D(1,1)) = // upper left and lower right
128  0.125, 0.0, -0.125,
129  0.25, 0.0, -0.25,
130  0.125, 0.0, -0.125;
131  sobel.setBorderTreatment(vigra::BORDER_TREATMENT_REFLECT);
132 
133  vigra::convolveImage(srcImageRange(src), destImage(dest), kernel2d(sobel));
134  \endcode
135 
136  <b> Required Interface:</b>
137 
138  \code
139  ImageIterator src_ul, src_lr;
140  ImageIterator dest_ul;
141  ImageIterator ik;
142 
143  SrcAccessor src_accessor;
144  DestAccessor dest_accessor;
145  KernelAccessor kernel_accessor;
146 
147  NumericTraits<SrcAccessor::value_type>::RealPromote s = src_accessor(src_ul);
148 
149  s = s + s;
150  s = kernel_accessor(ik) * s;
151  s -= s;
152 
153  dest_accessor.set(
154  NumericTraits<DestAccessor::value_type>::fromRealPromote(s), dest_ul);
155 
156  NumericTraits<KernelAccessor::value_type>::RealPromote k = kernel_accessor(ik);
157 
158  k += k;
159  k -= k;
160  k = k / k;
161 
162  \endcode
163 
164  <b> Preconditions:</b>
165 
166  \code
167  kul.x <= 0
168  kul.y <= 0
169  klr.x >= 0
170  klr.y >= 0
171  src_lr.x - src_ul.x >= klr.x + kul.x + 1
172  src_lr.y - src_ul.y >= klr.y + kul.y + 1
173  \endcode
174 
175  If border == BORDER_TREATMENT_CLIP: Sum of kernel elements must be
176  != 0.
177 
178 */
179 template <class SrcIterator, class SrcAccessor,
180  class DestIterator, class DestAccessor,
181  class KernelIterator, class KernelAccessor>
182 void convolveImage(SrcIterator src_ul, SrcIterator src_lr, SrcAccessor src_acc,
183  DestIterator dest_ul, DestAccessor dest_acc,
184  KernelIterator ki, KernelAccessor ak,
185  Diff2D kul, Diff2D klr, BorderTreatmentMode border)
186 {
187  vigra_precondition((border == BORDER_TREATMENT_CLIP ||
188  border == BORDER_TREATMENT_AVOID ||
189  border == BORDER_TREATMENT_REFLECT ||
190  border == BORDER_TREATMENT_REPEAT ||
191  border == BORDER_TREATMENT_WRAP ||
192  border == BORDER_TREATMENT_ZEROPAD),
193  "convolveImage():\n"
194  " Border treatment must be one of follow treatments:\n"
195  " - BORDER_TREATMENT_CLIP\n"
196  " - BORDER_TREATMENT_AVOID\n"
197  " - BORDER_TREATMENT_REFLECT\n"
198  " - BORDER_TREATMENT_REPEAT\n"
199  " - BORDER_TREATMENT_WRAP\n"
200  " - BORDER_TREATMENT_ZEROPAD\n");
201 
202  vigra_precondition(kul.x <= 0 && kul.y <= 0,
203  "convolveImage(): coordinates of "
204  "kernel's upper left must be <= 0.");
205  vigra_precondition(klr.x >= 0 && klr.y >= 0,
206  "convolveImage(): coordinates of "
207  "kernel's lower right must be >= 0.");
208 
209  // use traits to determine SumType as to prevent possible overflow
210  typedef typename
211  PromoteTraits<typename SrcAccessor::value_type,
212  typename KernelAccessor::value_type>::Promote SumType;
213  typedef typename
214  NumericTraits<typename KernelAccessor::value_type>::RealPromote KernelSumType;
215  typedef typename DestAccessor::value_type DestType;
216 
217  // calculate width and height of the image
218  int w = src_lr.x - src_ul.x;
219  int h = src_lr.y - src_ul.y;
220 
221  // calculate width and height of the kernel
222  int kernel_width = klr.x - kul.x + 1;
223  int kernel_height = klr.y - kul.y + 1;
224 
225  vigra_precondition(w >= std::max(klr.x, -kul.x) + 1 && h >= std::max(klr.y, -kul.y) + 1,
226  "convolveImage(): kernel larger than image.");
227 
228  KernelSumType norm = KernelSumType();
229  if(border == BORDER_TREATMENT_CLIP)
230  {
231  // calculate the sum of the kernel elements for renormalization
232  KernelIterator yk = ki + klr;
233 
234  // determine sum within kernel (= norm)
235  for(int y = 0; y < kernel_height; ++y, --yk.y)
236  {
237  KernelIterator xk = yk;
238  for(int x = 0; x < kernel_width; ++x, --xk.x)
239  {
240  norm += ak(xk);
241  }
242  }
243  vigra_precondition(norm != NumericTraits<KernelSumType>::zero(),
244  "convolveImage(): Cannot use BORDER_TREATMENT_CLIP with a DC-free kernel");
245  }
246 
247  DestIterator yd = dest_ul;
248  SrcIterator ys = src_ul;
249 
250  // iterate over the interior part
251  for(int y=0; y<h; ++y, ++ys.y, ++yd.y)
252  {
253  // create x iterators
254  DestIterator xd(yd);
255  SrcIterator xs(ys);
256 
257  for(int x=0; x < w; ++x, ++xs.x, ++xd.x)
258  {
259  // init the sum
260  SumType sum = NumericTraits<SumType>::zero();
261  KernelIterator ykernel = ki + klr;
262 
263  if(x >= klr.x && y >= klr.y && x < w + kul.x && y < h + kul.y)
264  {
265  // kernel is entirely inside the image
266  SrcIterator yys = xs - klr;
267  SrcIterator yyend = xs - kul;
268 
269  for(; yys.y <= yyend.y; ++yys.y, --ykernel.y)
270  {
271  typename SrcIterator::row_iterator xxs = yys.rowIterator();
272  typename SrcIterator::row_iterator xxe = xxs + kernel_width;
273  typename KernelIterator::row_iterator xkernel= ykernel.rowIterator();
274 
275  for(; xxs < xxe; ++xxs, --xkernel)
276  {
277  sum += ak(xkernel) * src_acc(xxs);
278  }
279  }
280  }
281  else if(border == BORDER_TREATMENT_REPEAT)
282  {
283  Diff2D diff;
284  for(int yk = klr.y; yk >= kul.y; --yk, --ykernel.y)
285  {
286  diff.y = std::min(std::max(y - yk, 0), h-1);
287  typename KernelIterator::row_iterator xkernel = ykernel.rowIterator();
288 
289  for(int xk = klr.x; xk >= kul.x; --xk, --xkernel)
290  {
291  diff.x = std::min(std::max(x - xk, 0), w-1);
292  sum += ak(xkernel) * src_acc(src_ul, diff);
293  }
294  }
295  }
296  else if(border == BORDER_TREATMENT_REFLECT)
297  {
298  Diff2D diff;
299  for(int yk = klr.y; yk >= kul.y; --yk , --ykernel.y)
300  {
301  diff.y = abs(y - yk);
302  if(diff.y >= h)
303  diff.y = 2*h - 2 - diff.y;
304  typename KernelIterator::row_iterator xkernel = ykernel.rowIterator();
305 
306  for(int xk = klr.x; xk >= kul.x; --xk, --xkernel)
307  {
308  diff.x = abs(x - xk);
309  if(diff.x >= w)
310  diff.x = 2*w - 2 - diff.x;
311  sum += ak(xkernel) * src_acc(src_ul, diff);
312  }
313  }
314  }
315  else if(border == BORDER_TREATMENT_WRAP)
316  {
317  Diff2D diff;
318  for(int yk = klr.y; yk >= kul.y; --yk, --ykernel.y)
319  {
320  diff.y = (y - yk + h) % h;
321  typename KernelIterator::row_iterator xkernel = ykernel.rowIterator();
322 
323  for(int xk = klr.x; xk >= kul.x; --xk, --xkernel)
324  {
325  diff.x = (x - xk + w) % w;
326  sum += ak(xkernel) * src_acc(src_ul, diff);
327  }
328  }
329  }
330  else if(border == BORDER_TREATMENT_CLIP)
331  {
332  KernelSumType ksum = NumericTraits<KernelSumType>::zero();
333  Diff2D diff;
334  for(int yk = klr.y; yk >= kul.y; --yk, --ykernel.y)
335  {
336  diff.y = y - yk;
337  if(diff.y < 0 || diff.y >= h)
338  continue;
339  typename KernelIterator::row_iterator xkernel = ykernel.rowIterator();
340 
341  for(int xk = klr.x; xk >= kul.x; --xk, --xkernel)
342  {
343  diff.x = x - xk;
344  if(diff.x < 0 || diff.x >= w)
345  continue;
346  ksum += ak(xkernel);
347  sum += ak(xkernel) * src_acc(src_ul, diff);
348  }
349  }
350 
351  sum *= norm / ksum;
352  }
353  else if(border == BORDER_TREATMENT_ZEROPAD)
354  {
355  Diff2D diff;
356  for(int yk = klr.y; yk >= kul.y; --yk, --ykernel.y)
357  {
358  diff.y = y - yk;
359  if(diff.y < 0 || diff.y >= h)
360  continue;
361  typename KernelIterator::row_iterator xkernel = ykernel.rowIterator();
362 
363  for(int xk = klr.x; xk >= kul.x; --xk, --xkernel)
364  {
365  diff.x = x - xk;
366  if(diff.x < 0 || diff.x >= w)
367  continue;
368  sum += ak(xkernel) * src_acc(src_ul, diff);
369  }
370  }
371  }
372  else if(border == BORDER_TREATMENT_AVOID)
373  {
374  continue;
375  }
376 
377  // store convolution result in destination pixel
378  dest_acc.set(detail::RequiresExplicitCast<DestType>::cast(sum), xd);
379  }
380  }
381 }
382 
383 template <class SrcIterator, class SrcAccessor,
384  class DestIterator, class DestAccessor,
385  class KernelIterator, class KernelAccessor>
386 inline
387 void convolveImage(
388  triple<SrcIterator, SrcIterator, SrcAccessor> src,
389  pair<DestIterator, DestAccessor> dest,
390  tuple5<KernelIterator, KernelAccessor, Diff2D, Diff2D,
391  BorderTreatmentMode> kernel)
392 {
393  convolveImage(src.first, src.second, src.third,
394  dest.first, dest.second,
395  kernel.first, kernel.second, kernel.third,
396  kernel.fourth, kernel.fifth);
397 }
398 
399 
400 /** \brief Performs a 2-dimensional normalized convolution, i.e. convolution with a mask image.
401 
402  This functions computes
403  <a href ="http://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/PIRODDI1/NormConv/NormConv.html">normalized
404  convolution</a> as defined in
405  Knutsson, H. and Westin, C-F.: <i>Normalized and differential convolution:
406  Methods for Interpolation and Filtering of incomplete and uncertain data</i>.
407  Proc. of the IEEE Conf. on Computer Vision and Pattern Recognition, 1993, 515-523.
408 
409  The mask image must be binary and encodes which pixels of the original image
410  are valid. It is used as follows:
411  Only pixel under the mask are used in the calculations. Whenever a part of the
412  kernel lies outside the mask, it is ignored, and the kernel is renormalized to its
413  original norm (analogous to the CLIP \ref BorderTreatmentMode). Thus, a useful convolution
414  result is computed whenever <i>at least one valid pixel is within the current window</i>
415  Thus, destination pixels not under the mask still receive a value if they are <i>near</i>
416  the mask. Therefore, this algorithm is useful as an interpolator of sparse input data.
417  If you are only interested in the destination values under the mask, you can perform
418  a subsequent \ref copyImageIf().
419 
420  The KernelIterator must point to the center of the kernel, and
421  the kernel's size is given by its upper left (x and y of distance <= 0) and
422  lower right (distance >= 0) corners. The image must always be larger than the
423  kernel. At those positions where the kernel does not completely fit
424  into the image, the specified \ref BorderTreatmentMode is
425  applied. Only BORDER_TREATMENT_CLIP and BORDER_TREATMENT_AVOID are currently
426  supported.
427 
428  The images's pixel type (SrcAccessor::value_type) must be a
429  linear space over the kernel's value_type (KernelAccessor::value_type),
430  i.e. addition of source values, multiplication with kernel values,
431  and NumericTraits must be defined.
432  The kernel's value_type must be an algebraic field,
433  i.e. the arithmetic operations (+, -, *, /) and NumericTraits must
434  be defined.
435 
436  <b> Declarations:</b>
437 
438  pass arguments explicitly:
439  \code
440  namespace vigra {
441  template <class SrcIterator, class SrcAccessor,
442  class MaskIterator, class MaskAccessor,
443  class DestIterator, class DestAccessor,
444  class KernelIterator, class KernelAccessor>
445  void
446  normalizedConvolveImage(SrcIterator src_ul, SrcIterator src_lr, SrcAccessor src_acc,
447  MaskIterator mul, MaskAccessor am,
448  DestIterator dest_ul, DestAccessor dest_acc,
449  KernelIterator ki, KernelAccessor ak,
450  Diff2D kul, Diff2D klr, BorderTreatmentMode border);
451  }
452  \endcode
453 
454 
455  use argument objects in conjunction with \ref ArgumentObjectFactories :
456  \code
457  namespace vigra {
458  template <class SrcIterator, class SrcAccessor,
459  class MaskIterator, class MaskAccessor,
460  class DestIterator, class DestAccessor,
461  class KernelIterator, class KernelAccessor>
462  void normalizedConvolveImage(triple<SrcIterator, SrcIterator, SrcAccessor> src,
463  pair<MaskIterator, MaskAccessor> mask,
464  pair<DestIterator, DestAccessor> dest,
465  tuple5<KernelIterator, KernelAccessor, Diff2D, Diff2D,
466  BorderTreatmentMode> kernel);
467  }
468  \endcode
469 
470  <b> Usage:</b>
471 
472  <b>\#include</b> <vigra/stdconvolution.hxx><br>
473  Namespace: vigra
474 
475 
476  \code
477  vigra::FImage src(w,h), dest(w,h);
478  vigra::CImage mask(w,h);
479  ...
480 
481  // define 3x3 binomial filter
482  vigra::Kernel2D<float> binom;
483 
484  binom.initExplicitly(Diff2D(-1,-1), Diff2D(1,1)) = // upper left and lower right
485  0.0625, 0.125, 0.0625,
486  0.125, 0.25, 0.125,
487  0.0625, 0.125, 0.0625;
488 
489  vigra::normalizedConvolveImage(srcImageRange(src), maskImage(mask), destImage(dest), kernel2d(binom));
490  \endcode
491 
492  <b> Required Interface:</b>
493 
494  \code
495  ImageIterator src_ul, src_lr;
496  ImageIterator mul;
497  ImageIterator dest_ul;
498  ImageIterator ik;
499 
500  SrcAccessor src_accessor;
501  MaskAccessor mask_accessor;
502  DestAccessor dest_accessor;
503  KernelAccessor kernel_accessor;
504 
505  NumericTraits<SrcAccessor::value_type>::RealPromote s = src_accessor(src_ul);
506 
507  s = s + s;
508  s = kernel_accessor(ik) * s;
509  s -= s;
510 
511  if(mask_accessor(mul)) ...;
512 
513  dest_accessor.set(
514  NumericTraits<DestAccessor::value_type>::fromRealPromote(s), dest_ul);
515 
516  NumericTraits<KernelAccessor::value_type>::RealPromote k = kernel_accessor(ik);
517 
518  k += k;
519  k -= k;
520  k = k / k;
521 
522  \endcode
523 
524  <b> Preconditions:</b>
525 
526  \code
527  kul.x <= 0
528  kul.y <= 0
529  klr.x >= 0
530  klr.y >= 0
531  src_lr.x - src_ul.x >= klr.x + kul.x + 1
532  src_lr.y - src_ul.y >= klr.y + kul.y + 1
533  border == BORDER_TREATMENT_CLIP || border == BORDER_TREATMENT_AVOID
534  \endcode
535 
536  Sum of kernel elements must be != 0.
537 
538 */
539 doxygen_overloaded_function(template <...> void normalizedConvolveImage)
540 
541 template <class SrcIterator, class SrcAccessor,
542  class DestIterator, class DestAccessor,
543  class MaskIterator, class MaskAccessor,
544  class KernelIterator, class KernelAccessor>
545 void
546 normalizedConvolveImage(SrcIterator src_ul, SrcIterator src_lr, SrcAccessor src_acc,
547  MaskIterator mul, MaskAccessor am,
548  DestIterator dest_ul, DestAccessor dest_acc,
549  KernelIterator ki, KernelAccessor ak,
550  Diff2D kul, Diff2D klr, BorderTreatmentMode border)
551 {
552  vigra_precondition((border == BORDER_TREATMENT_CLIP ||
553  border == BORDER_TREATMENT_AVOID),
554  "normalizedConvolveImage(): "
555  "Border treatment must be BORDER_TREATMENT_CLIP or BORDER_TREATMENT_AVOID.");
556 
557  vigra_precondition(kul.x <= 0 && kul.y <= 0,
558  "normalizedConvolveImage(): left borders must be <= 0.");
559  vigra_precondition(klr.x >= 0 && klr.y >= 0,
560  "normalizedConvolveImage(): right borders must be >= 0.");
561 
562  // use traits to determine SumType as to prevent possible overflow
563  typedef typename
564  NumericTraits<typename SrcAccessor::value_type>::RealPromote SumType;
565  typedef typename
566  NumericTraits<typename KernelAccessor::value_type>::RealPromote KSumType;
567  typedef
568  NumericTraits<typename DestAccessor::value_type> DestTraits;
569 
570  // calculate width and height of the image
571  int w = src_lr.x - src_ul.x;
572  int h = src_lr.y - src_ul.y;
573  int kernel_width = klr.x - kul.x + 1;
574  int kernel_height = klr.y - kul.y + 1;
575 
576  int x,y;
577  int ystart = (border == BORDER_TREATMENT_AVOID) ? klr.y : 0;
578  int yend = (border == BORDER_TREATMENT_AVOID) ? h+kul.y : h;
579  int xstart = (border == BORDER_TREATMENT_AVOID) ? klr.x : 0;
580  int xend = (border == BORDER_TREATMENT_AVOID) ? w+kul.x : w;
581 
582  // create y iterators
583  DestIterator yd = dest_ul + Diff2D(xstart, ystart);
584  SrcIterator ys = src_ul + Diff2D(xstart, ystart);
585  MaskIterator ym = mul + Diff2D(xstart, ystart);
586 
587  KSumType norm = ak(ki);
588  int xx, yy;
589  KernelIterator yk = ki + klr;
590  for(yy=0; yy<kernel_height; ++yy, --yk.y)
591  {
592  KernelIterator xk = yk;
593 
594  for(xx=0; xx<kernel_width; ++xx, --xk.x)
595  {
596  norm += ak(xk);
597  }
598  }
599  norm -= ak(ki);
600 
601 
602  for(y=ystart; y < yend; ++y, ++ys.y, ++yd.y, ++ym.y)
603  {
604  // create x iterators
605  DestIterator xd(yd);
606  SrcIterator xs(ys);
607  MaskIterator xm(ym);
608 
609  for(x=xstart; x < xend; ++x, ++xs.x, ++xd.x, ++xm.x)
610  {
611  // how much of the kernel fits into the image ?
612  int x0, y0, x1, y1;
613 
614  y0 = (y<klr.y) ? -y : -klr.y;
615  y1 = (h-y-1<-kul.y) ? h-y-1 : -kul.y;
616  x0 = (x<klr.x) ? -x : -klr.x;
617  x1 = (w-x-1<-kul.x) ? w-x-1 : -kul.x;
618 
619  bool first = true;
620  // init the sum
621  SumType sum = NumericTraits<SumType>::zero();
622  KSumType ksum = NumericTraits<KSumType>::zero();
623 
624  SrcIterator yys = xs + Diff2D(x0, y0);
625  MaskIterator yym = xm + Diff2D(x0, y0);
626  KernelIterator yk = ki - Diff2D(x0, y0);
627 
628  int kernel_width, kernel_height;
629  kernel_width = x1 - x0 + 1;
630  kernel_height = y1 - y0 + 1;
631  for(yy=0; yy<kernel_height; ++yy, ++yys.y, --yk.y, ++yym.y)
632  {
633  typename SrcIterator::row_iterator xxs = yys.rowIterator();
634  typename SrcIterator::row_iterator xxend = xxs + kernel_width;
635  typename MaskIterator::row_iterator xxm = yym.rowIterator();
636  typename KernelIterator::row_iterator xk = yk.rowIterator();
637 
638  for(xx=0; xxs < xxend; ++xxs, --xk, ++xxm)
639  {
640  if(!am(xxm)) continue;
641 
642  if(first)
643  {
644  sum = detail::RequiresExplicitCast<SumType>::cast(ak(xk) * src_acc(xxs));
645  ksum = ak(xk);
646  first = false;
647  }
648  else
649  {
650  sum = detail::RequiresExplicitCast<SumType>::cast(sum + ak(xk) * src_acc(xxs));
651  ksum += ak(xk);
652  }
653  }
654  }
655  // store average in destination pixel
656  if(ksum != NumericTraits<KSumType>::zero())
657  {
658  dest_acc.set(DestTraits::fromRealPromote(
659  detail::RequiresExplicitCast<SumType>::cast((norm / ksum) * sum)), xd);
660  }
661  }
662  }
663 }
664 
665 
666 template <class SrcIterator, class SrcAccessor,
667  class DestIterator, class DestAccessor,
668  class MaskIterator, class MaskAccessor,
669  class KernelIterator, class KernelAccessor>
670 inline
672  triple<SrcIterator, SrcIterator, SrcAccessor> src,
673  pair<MaskIterator, MaskAccessor> mask,
674  pair<DestIterator, DestAccessor> dest,
675  tuple5<KernelIterator, KernelAccessor, Diff2D, Diff2D,
676  BorderTreatmentMode> kernel)
677 {
678  normalizedConvolveImage(src.first, src.second, src.third,
679  mask.first, mask.second,
680  dest.first, dest.second,
681  kernel.first, kernel.second, kernel.third,
682  kernel.fourth, kernel.fifth);
683 }
684 
685 /** \brief Deprecated name of 2-dimensional normalized convolution, i.e. convolution with a mask image.
686 
687  See \ref normalizedConvolveImage() for documentation.
688 
689  <b> Declarations:</b>
690 
691  pass arguments explicitly:
692  \code
693  namespace vigra {
694  template <class SrcIterator, class SrcAccessor,
695  class MaskIterator, class MaskAccessor,
696  class DestIterator, class DestAccessor,
697  class KernelIterator, class KernelAccessor>
698  void
699  convolveImageWithMask(SrcIterator src_ul, SrcIterator src_lr, SrcAccessor src_acc,
700  MaskIterator mul, MaskAccessor am,
701  DestIterator dest_ul, DestAccessor dest_acc,
702  KernelIterator ki, KernelAccessor ak,
703  Diff2D kul, Diff2D klr, BorderTreatmentMode border);
704  }
705  \endcode
706 
707 
708  use argument objects in conjunction with \ref ArgumentObjectFactories :
709  \code
710  namespace vigra {
711  template <class SrcIterator, class SrcAccessor,
712  class MaskIterator, class MaskAccessor,
713  class DestIterator, class DestAccessor,
714  class KernelIterator, class KernelAccessor>
715  void convolveImageWithMask(triple<SrcIterator, SrcIterator, SrcAccessor> src,
716  pair<MaskIterator, MaskAccessor> mask,
717  pair<DestIterator, DestAccessor> dest,
718  tuple5<KernelIterator, KernelAccessor, Diff2D, Diff2D,
719  BorderTreatmentMode> kernel);
720  }
721  \endcode
722 */
723 doxygen_overloaded_function(template <...> void convolveImageWithMask)
724 
725 template <class SrcIterator, class SrcAccessor,
726  class DestIterator, class DestAccessor,
727  class MaskIterator, class MaskAccessor,
728  class KernelIterator, class KernelAccessor>
729 inline void
730 convolveImageWithMask(SrcIterator src_ul, SrcIterator src_lr, SrcAccessor src_acc,
731  MaskIterator mul, MaskAccessor am,
732  DestIterator dest_ul, DestAccessor dest_acc,
733  KernelIterator ki, KernelAccessor ak,
734  Diff2D kul, Diff2D klr, BorderTreatmentMode border)
735 {
736  normalizedConvolveImage(src_ul, src_lr, src_acc,
737  mul, am,
738  dest_ul, dest_acc,
739  ki, ak, kul, klr, border);
740 }
741 
742 template <class SrcIterator, class SrcAccessor,
743  class DestIterator, class DestAccessor,
744  class MaskIterator, class MaskAccessor,
745  class KernelIterator, class KernelAccessor>
746 inline
748  triple<SrcIterator, SrcIterator, SrcAccessor> src,
749  pair<MaskIterator, MaskAccessor> mask,
750  pair<DestIterator, DestAccessor> dest,
751  tuple5<KernelIterator, KernelAccessor, Diff2D, Diff2D,
752  BorderTreatmentMode> kernel)
753 {
754  normalizedConvolveImage(src.first, src.second, src.third,
755  mask.first, mask.second,
756  dest.first, dest.second,
757  kernel.first, kernel.second, kernel.third,
758  kernel.fourth, kernel.fifth);
759 }
760 
761 //@}
762 
763 /********************************************************/
764 /* */
765 /* Kernel2D */
766 /* */
767 /********************************************************/
768 
769 /** \brief Generic 2 dimensional convolution kernel.
770 
771  This kernel may be used for convolution of 2 dimensional signals.
772 
773  Convolution functions access the kernel via an ImageIterator
774  which they get by calling \ref center(). This iterator
775  points to the center of the kernel. The kernel's size is given by its upperLeft()
776  (upperLeft().x <= 0, upperLeft().y <= 0)
777  and lowerRight() (lowerRight().x >= 0, lowerRight().y >= 0) methods.
778  The desired border treatment mode is returned by borderTreatment().
779  (Note that the \ref StandardConvolution "2D convolution functions" don't currently
780  support all modes.)
781 
782  The different init functions create a kernel with the specified
783  properties. The requirements for the kernel's value_type depend
784  on the init function used. At least NumericTraits must be defined.
785 
786  The kernel defines a factory function kernel2d() to create an argument object
787  (see \ref KernelArgumentObjectFactories).
788 
789  <b> Usage:</b>
790 
791  <b>\#include</b> <vigra/stdconvolution.hxx><br>
792  Namespace: vigra
793 
794  \code
795  vigra::FImage src(w,h), dest(w,h);
796  ...
797 
798  // define horizontal Sobel filter
799  vigra::Kernel2D<float> sobel;
800 
801  sobel.initExplicitly(Diff2D(-1,-1), Diff2D(1,1)) = // upper left and lower right
802  0.125, 0.0, -0.125,
803  0.25, 0.0, -0.25,
804  0.125, 0.0, -0.125;
805 
806  vigra::convolveImage(srcImageRange(src), destImage(dest), kernel2d(sobel));
807  \endcode
808 
809  <b> Required Interface:</b>
810 
811  \code
812  value_type v = NumericTraits<value_type>::one();
813  \endcode
814 
815  See also the init functions.
816 */
817 template <class ARITHTYPE>
818 class Kernel2D
819 {
820 public:
821  /** the kernel's value type
822  */
823  typedef ARITHTYPE value_type;
824 
825  /** 2D random access iterator over the kernel's values
826  */
828 
829  /** const 2D random access iterator over the kernel's values
830  */
832 
833  /** the kernel's accessor
834  */
836 
837  /** the kernel's const accessor
838  */
840 
841  struct InitProxy
842  {
843  typedef typename
845 
846  InitProxy(Iterator i, int count, value_type & norm)
847  : iter_(i), base_(i),
848  count_(count), sum_(count),
849  norm_(norm)
850  {}
851 
852  ~InitProxy()
853  {
854  vigra_precondition(count_ == 1 || count_ == sum_,
855  "Kernel2D::initExplicitly(): "
856  "Too few init values.");
857  }
858 
859  InitProxy & operator,(value_type const & v)
860  {
861  if(count_ == sum_) norm_ = *iter_;
862 
863  --count_;
864  vigra_precondition(count_ > 0,
865  "Kernel2D::initExplicitly(): "
866  "Too many init values.");
867 
868  norm_ += v;
869 
870  ++iter_;
871  *iter_ = v;
872 
873  return *this;
874  }
875 
876  Iterator iter_, base_;
877  int count_, sum_;
878  value_type & norm_;
879  };
880 
881  static value_type one() { return NumericTraits<value_type>::one(); }
882 
883  /** Default constructor.
884  Creates a kernel of size 1x1 which would copy the signal
885  unchanged.
886  */
888  : kernel_(1, 1, one()),
889  left_(0, 0),
890  right_(0, 0),
891  norm_(one()),
892  border_treatment_(BORDER_TREATMENT_REFLECT)
893  {}
894 
895  /** Copy constructor.
896  */
897  Kernel2D(Kernel2D const & k)
898  : kernel_(k.kernel_),
899  left_(k.left_),
900  right_(k.right_),
901  norm_(k.norm_),
902  border_treatment_(k.border_treatment_)
903  {}
904 
905  /** Copy assignment.
906  */
908  {
909  if(this != &k)
910  {
911  kernel_ = k.kernel_;
912  left_ = k.left_;
913  right_ = k.right_;
914  norm_ = k.norm_;
915  border_treatment_ = k.border_treatment_;
916  }
917  return *this;
918  }
919 
920  /** Initialization.
921  This initializes the kernel with the given constant. The norm becomes
922  v*width()*height().
923 
924  Instead of a single value an initializer list of length width()*height()
925  can be used like this:
926 
927  \code
928  vigra::Kernel2D<float> binom;
929 
930  binom.initExplicitly(Diff2D(-1,-1), Diff2D(1,1)) =
931  0.0625, 0.125, 0.0625,
932  0.125, 0.25, 0.125,
933  0.0625, 0.125, 0.0625;
934  \endcode
935 
936  In this case, the norm will be set to the sum of the init values.
937  An initializer list of wrong length will result in a run-time error.
938  */
939  InitProxy operator=(value_type const & v)
940  {
941  int size = (right_.x - left_.x + 1) *
942  (right_.y - left_.y + 1);
943  kernel_ = v;
944  norm_ = (double)size*v;
945 
946  return InitProxy(kernel_.begin(), size, norm_);
947  }
948 
949  /** Destructor.
950  */
952  {}
953 
954  /** Init the 2D kernel as the cartesian product of two 1D kernels
955  of type \ref Kernel1D. The norm becomes the product of the two original
956  norms.
957 
958  <b> Required Interface:</b>
959 
960  The kernel's value_type must be a linear algebra.
961 
962  \code
963  vigra::Kernel2D<...>::value_type v;
964  v = v * v;
965  \endcode
966  */
968  Kernel1D<value_type> const & ky)
969  {
970  left_ = Diff2D(kx.left(), ky.left());
971  right_ = Diff2D(kx.right(), ky.right());
972  int w = right_.x - left_.x + 1;
973  int h = right_.y - left_.y + 1;
974  kernel_.resize(w, h);
975 
976  norm_ = kx.norm() * ky.norm();
977 
978  typedef typename Kernel1D<value_type>::const_iterator KIter;
979  typename Kernel1D<value_type>::Accessor ka;
980 
981  KIter kiy = ky.center() + left_.y;
982  Iterator iy = center() + left_;
983 
984  for(int y=left_.y; y<=right_.y; ++y, ++kiy, ++iy.y)
985  {
986  KIter kix = kx.center() + left_.x;
987  Iterator ix = iy;
988  for(int x=left_.x; x<=right_.x; ++x, ++kix, ++ix.x)
989  {
990  *ix = ka(kix) * ka(kiy);
991  }
992  }
993  }
994 
995  /** Init the 2D kernel as the cartesian product of two 1D kernels
996  given explicitly by iterators and sizes. The norm becomes the
997  sum of the resulting kernel values.
998 
999  <b> Required Interface:</b>
1000 
1001  The kernel's value_type must be a linear algebra.
1002 
1003  \code
1004  vigra::Kernel2D<...>::value_type v;
1005  v = v * v;
1006  v += v;
1007  \endcode
1008 
1009  <b> Preconditions:</b>
1010 
1011  \code
1012  xleft <= 0;
1013  xright >= 0;
1014  yleft <= 0;
1015  yright >= 0;
1016  \endcode
1017  */
1018  template <class KernelIterator>
1019  void initSeparable(KernelIterator kxcenter, int xleft, int xright,
1020  KernelIterator kycenter, int yleft, int yright)
1021  {
1022  vigra_precondition(xleft <= 0 && yleft <= 0,
1023  "Kernel2D::initSeparable(): left borders must be <= 0.");
1024  vigra_precondition(xright >= 0 && yright >= 0,
1025  "Kernel2D::initSeparable(): right borders must be >= 0.");
1026 
1027  left_ = Point2D(xleft, yleft);
1028  right_ = Point2D(xright, yright);
1029 
1030  int w = right_.x - left_.x + 1;
1031  int h = right_.y - left_.y + 1;
1032  kernel_.resize(w, h);
1033 
1034  KernelIterator kiy = kycenter + left_.y;
1035  Iterator iy = center() + left_;
1036 
1037  for(int y=left_.y; y<=right_.y; ++y, ++kiy, ++iy.y)
1038  {
1039  KernelIterator kix = kxcenter + left_.x;
1040  Iterator ix = iy;
1041  for(int x=left_.x; x<=right_.x; ++x, ++kix, ++ix.x)
1042  {
1043  *ix = *kix * *kiy;
1044  }
1045  }
1046 
1047  typename BasicImage<value_type>::iterator i = kernel_.begin();
1048  typename BasicImage<value_type>::iterator iend = kernel_.end();
1049  norm_ = *i;
1050  ++i;
1051 
1052  for(; i!= iend; ++i)
1053  {
1054  norm_ += *i;
1055  }
1056  }
1057 
1058  /** Init as a 2D Gaussian function with given standard deviation and norm.
1059  */
1060  void initGaussian(double std_dev, value_type norm)
1061  {
1062  Kernel1D<value_type> gauss;
1063  gauss.initGaussian(std_dev, norm);
1064  initSeparable(gauss, gauss);
1065  }
1066 
1067  /** Init as a 2D Gaussian function with given standard deviation and unit norm.
1068  */
1069  void initGaussian(double std_dev)
1070  {
1071  initGaussian(std_dev, NumericTraits<value_type>::one());
1072  }
1073 
1074  /** Init the 2D kernel as a circular averaging filter. The norm will be
1075  calculated as
1076  <TT>NumericTraits<value_type>::one() / (number of non-zero kernel values)</TT>.
1077  The kernel's value_type must be a linear space.
1078 
1079  <b> Required Interface:</b>
1080 
1081  \code
1082  value_type v = vigra::NumericTraits<value_type>::one();
1083 
1084  double d;
1085  v = d * v;
1086  \endcode
1087 
1088  <b> Precondition:</b>
1089 
1090  \code
1091  radius > 0;
1092  \endcode
1093  */
1094  void initDisk(int radius)
1095  {
1096  vigra_precondition(radius > 0,
1097  "Kernel2D::initDisk(): radius must be > 0.");
1098 
1099  left_ = Point2D(-radius, -radius);
1100  right_ = Point2D(radius, radius);
1101  int w = right_.x - left_.x + 1;
1102  int h = right_.y - left_.y + 1;
1103  kernel_.resize(w, h);
1104  norm_ = NumericTraits<value_type>::one();
1105 
1106  kernel_ = NumericTraits<value_type>::zero();
1107  double count = 0.0;
1108 
1109  Iterator k = center();
1110  double r2 = (double)radius*radius;
1111 
1112  int i;
1113  for(i=0; i<= radius; ++i)
1114  {
1115  double r = (double) i - 0.5;
1116  int w = (int)(VIGRA_CSTD::sqrt(r2 - r*r) + 0.5);
1117  for(int j=-w; j<=w; ++j)
1118  {
1119  k(j, i) = NumericTraits<value_type>::one();
1120  k(j, -i) = NumericTraits<value_type>::one();
1121  count += (i != 0) ? 2.0 : 1.0;
1122  }
1123  }
1124 
1125  count = 1.0 / count;
1126 
1127  for(int y=-radius; y<=radius; ++y)
1128  {
1129  for(int x=-radius; x<=radius; ++x)
1130  {
1131  k(x,y) = count * k(x,y);
1132  }
1133  }
1134  }
1135 
1136  /** Init the kernel by an explicit initializer list.
1137  The upper left and lower right corners of the kernel must be passed.
1138  A comma-separated initializer list is given after the assignment operator.
1139  This function is used like this:
1140 
1141  \code
1142  // define horizontal Sobel filter
1143  vigra::Kernel2D<float> sobel;
1144 
1145  sobel.initExplicitly(Diff2D(-1,-1), Diff2D(1,1)) =
1146  0.125, 0.0, -0.125,
1147  0.25, 0.0, -0.25,
1148  0.125, 0.0, -0.125;
1149  \endcode
1150 
1151  The norm is set to the sum of the initializer values. If the wrong number of
1152  values is given, a run-time error results. It is, however, possible to give
1153  just one initializer. This creates an averaging filter with the given constant:
1154 
1155  \code
1156  vigra::Kernel2D<float> average3x3;
1157 
1158  average3x3.initExplicitly(Diff2D(-1,-1), Diff2D(1,1)) = 1.0/9.0;
1159  \endcode
1160 
1161  Here, the norm is set to value*width()*height().
1162 
1163  <b> Preconditions:</b>
1164 
1165  \code
1166  1. upperleft.x <= 0;
1167  2. upperleft.y <= 0;
1168  3. lowerright.x >= 0;
1169  4. lowerright.y >= 0;
1170  5. the number of values in the initializer list
1171  is 1 or equals the size of the kernel.
1172  \endcode
1173  */
1174  Kernel2D & initExplicitly(Diff2D upperleft, Diff2D lowerright)
1175  {
1176  vigra_precondition(upperleft.x <= 0 && upperleft.y <= 0,
1177  "Kernel2D::initExplicitly(): left borders must be <= 0.");
1178  vigra_precondition(lowerright.x >= 0 && lowerright.y >= 0,
1179  "Kernel2D::initExplicitly(): right borders must be >= 0.");
1180 
1181  left_ = Point2D(upperleft);
1182  right_ = Point2D(lowerright);
1183 
1184  int w = right_.x - left_.x + 1;
1185  int h = right_.y - left_.y + 1;
1186  kernel_.resize(w, h);
1187 
1188  return *this;
1189  }
1190 
1191  /** Coordinates of the upper left corner of the kernel.
1192  */
1193  Point2D upperLeft() const { return left_; }
1194 
1195  /** Coordinates of the lower right corner of the kernel.
1196  */
1197  Point2D lowerRight() const { return right_; }
1198 
1199  /** Width of the kernel.
1200  */
1201  int width() const { return right_.x - left_.x + 1; }
1202 
1203  /** Height of the kernel.
1204  */
1205  int height() const { return right_.y - left_.y + 1; }
1206 
1207  /** ImageIterator that points to the center of the kernel (coordinate (0,0)).
1208  */
1209  Iterator center() { return kernel_.upperLeft() - left_; }
1210 
1211  /** ImageIterator that points to the center of the kernel (coordinate (0,0)).
1212  */
1213  ConstIterator center() const { return kernel_.upperLeft() - left_; }
1214 
1215  /** Access kernel entry at given position.
1216  */
1217  value_type & operator()(int x, int y)
1218  { return kernel_[Diff2D(x,y) - left_]; }
1219 
1220  /** Read kernel entry at given position.
1221  */
1222  value_type operator()(int x, int y) const
1223  { return kernel_[Diff2D(x,y) - left_]; }
1224 
1225  /** Access kernel entry at given position.
1226  */
1228  { return kernel_[d - left_]; }
1229 
1230  /** Read kernel entry at given position.
1231  */
1232  value_type operator[](Diff2D const & d) const
1233  { return kernel_[d - left_]; }
1234 
1235  /** Norm of the kernel (i.e. sum of its elements).
1236  */
1237  value_type norm() const { return norm_; }
1238 
1239  /** The kernels default accessor.
1240  */
1241  Accessor accessor() { return Accessor(); }
1242 
1243  /** The kernels default const accessor.
1244  */
1245  ConstAccessor accessor() const { return ConstAccessor(); }
1246 
1247  /** Normalize the kernel to the given value. (The norm is the sum of all kernel
1248  elements.) The kernel's value_type must be a division algebra or
1249  algebraic field.
1250 
1251  <b> Required Interface:</b>
1252 
1253  \code
1254  value_type v = vigra::NumericTraits<value_type>::one(); // if norm is not
1255  // given explicitly
1256 
1257  v += v;
1258  v = v * v;
1259  v = v / v;
1260  \endcode
1261  */
1263  {
1264  typename BasicImage<value_type>::iterator i = kernel_.begin();
1265  typename BasicImage<value_type>::iterator iend = kernel_.end();
1266  typename NumericTraits<value_type>::RealPromote sum = *i;
1267  ++i;
1268 
1269  for(; i!= iend; ++i)
1270  {
1271  sum += *i;
1272  }
1273 
1274  sum = norm / sum;
1275  i = kernel_.begin();
1276  for(; i != iend; ++i)
1277  {
1278  *i = *i * sum;
1279  }
1280 
1281  norm_ = norm;
1282  }
1283 
1284  /** Normalize the kernel to norm 1.
1285  */
1286  void normalize()
1287  {
1288  normalize(one());
1289  }
1290 
1291  /** current border treatment mode
1292  */
1293  BorderTreatmentMode borderTreatment() const
1294  { return border_treatment_; }
1295 
1296  /** Set border treatment mode.
1297  Only <TT>BORDER_TREATMENT_CLIP</TT> and <TT>BORDER_TREATMENT_AVOID</TT> are currently
1298  allowed.
1299  */
1300  void setBorderTreatment( BorderTreatmentMode new_mode)
1301  {
1302  vigra_precondition((new_mode == BORDER_TREATMENT_CLIP ||
1303  new_mode == BORDER_TREATMENT_AVOID ||
1304  new_mode == BORDER_TREATMENT_REFLECT ||
1305  new_mode == BORDER_TREATMENT_REPEAT ||
1306  new_mode == BORDER_TREATMENT_WRAP),
1307  "convolveImage():\n"
1308  " Border treatment must be one of follow treatments:\n"
1309  " - BORDER_TREATMENT_CLIP\n"
1310  " - BORDER_TREATMENT_AVOID\n"
1311  " - BORDER_TREATMENT_REFLECT\n"
1312  " - BORDER_TREATMENT_REPEAT\n"
1313  " - BORDER_TREATMENT_WRAP\n");
1314 
1315  border_treatment_ = new_mode;
1316  }
1317 
1318 
1319 private:
1320  BasicImage<value_type> kernel_;
1321  Point2D left_, right_;
1322  value_type norm_;
1323  BorderTreatmentMode border_treatment_;
1324 };
1325 
1326 /**************************************************************/
1327 /* */
1328 /* Argument object factories for Kernel2D */
1329 /* */
1330 /* (documentation: see vigra/convolution.hxx) */
1331 /* */
1332 /**************************************************************/
1333 
1334 template <class KernelIterator, class KernelAccessor>
1335 inline
1336 tuple5<KernelIterator, KernelAccessor, Diff2D, Diff2D, BorderTreatmentMode>
1337 kernel2d(KernelIterator ik, KernelAccessor ak, Diff2D kul, Diff2D klr,
1338  BorderTreatmentMode border)
1339 
1340 {
1341  return
1342  tuple5<KernelIterator, KernelAccessor, Diff2D, Diff2D, BorderTreatmentMode> (
1343  ik, ak, kul, klr, border);
1344 }
1345 
1346 template <class T>
1347 inline
1348 tuple5<typename Kernel2D<T>::ConstIterator,
1349  typename Kernel2D<T>::ConstAccessor,
1350  Diff2D, Diff2D, BorderTreatmentMode>
1351 kernel2d(Kernel2D<T> const & k)
1352 
1353 {
1354  return
1355  tuple5<typename Kernel2D<T>::ConstIterator,
1356  typename Kernel2D<T>::ConstAccessor,
1357  Diff2D, Diff2D, BorderTreatmentMode>(
1358  k.center(),
1359  k.accessor(),
1360  k.upperLeft(), k.lowerRight(),
1361  k.borderTreatment());
1362 }
1363 
1364 template <class T>
1365 inline
1366 tuple5<typename Kernel2D<T>::ConstIterator,
1367  typename Kernel2D<T>::ConstAccessor,
1368  Diff2D, Diff2D, BorderTreatmentMode>
1369 kernel2d(Kernel2D<T> const & k, BorderTreatmentMode border)
1370 
1371 {
1372  return
1373  tuple5<typename Kernel2D<T>::ConstIterator,
1374  typename Kernel2D<T>::ConstAccessor,
1375  Diff2D, Diff2D, BorderTreatmentMode>(
1376  k.center(),
1377  k.accessor(),
1378  k.upperLeft(), k.lowerRight(),
1379  border);
1380 }
1381 
1382 
1383 } // namespace vigra
1384 
1385 #endif // VIGRA_STDCONVOLUTION_HXX

© Ullrich Köthe (ullrich.koethe@iwr.uni-heidelberg.de)
Heidelberg Collaboratory for Image Processing, University of Heidelberg, Germany

html generated using doxygen and Python
vigra 1.9.0 (Tue Oct 22 2013)