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