SLAMflex SE  0.1.0
SLAMflex provides detection and tracking of dominant planes for smartphone devices. This plane can then be used to show AR content relative to the plane orientation. The detection of plane is performed in the field of view of the smartphone camera. In subsequent frames it is tracked. The interface returns the plane position and orientation.
convolution.h
Go to the documentation of this file.
1 /*
2  This file is part of the CVD Library.
3 
4  Copyright (C) 2005 The Authors
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Lesser General Public
8  License as published by the Free Software Foundation; either
9  version 2.1 of the License, or (at your option) any later version.
10 
11  This library is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  Lesser General Public License for more details.
15 
16  You should have received a copy of the GNU Lesser General Public
17  License along with this library; if not, write to the Free Software
18  Foundation, Inc.,
19  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21 
22 #ifndef CVD_CONVOLUTION_H_
23 #define CVD_CONVOLUTION_H_
24 
25 #include <vector>
26 #include <memory>
27 #include <numeric>
28 #include <algorithm>
29 
30 #include "exceptions.h"
31 #include "image.h"
32 #include "pixel_operations.h"
33 #include "aligned_mem.h"
34 #include "utility.h"
35 
36 namespace CVD {
37 
46 template <class T>
47 T gaussianKernel(std::vector<T>& k, T maxval, double stddev)
48 {
49  double sum = 0;
50  unsigned int i, argmax=0;
51  std::vector<double> kernel(k.size());
52  for (i=0;i<k.size();i++) {
53  double x = i +0.5 - k.size()/2.0;
54  sum += kernel[i] = exp(-x*x/(2*stddev*stddev));
55  if (kernel[i] > kernel[argmax])
56  argmax = i;
57  }
58  T finalSum = 0;
59  for (i=0;i<k.size();i++)
60  finalSum += k[i] = (T)(kernel[i]*maxval/kernel[argmax]);
61  return finalSum;
62 }
63 
71 template <class S, class T>
72 T scaleKernel(const std::vector<S>& k, std::vector<T>& scaled, T maxval)
73 {
74  unsigned int i,argmax=0;
75  for (i=1;i<k.size();i++)
76  if (k[i]>k[argmax])
77  argmax = i;
78  scaled.resize(k.size());
79  T sum = 0;
80  for (i=0;i<k.size();i++)
81  sum += (scaled[i] = (T)((k[i]*maxval)/k[argmax]));
82  return sum;
83 }
84 
85 template <class T>
87 {
88  int w = I.size().x;
89  int h = I.size().y;
90  int i,j;
91  for (j=0;j<w;j++) {
92  T* src = I.data()+j;
93  T* end = src + w*(h-4);
94  while (src != end) {
95  T sum= (T)(0.0544887*(src[0]+src[4*w])
96  + 0.2442010*(src[w]+src[3*w])
97  + 0.4026200*src[2*w]);
98  *(src) = sum;
99  src += w;
100  }
101  }
102  for (i=h-5;i>=0;i--) {
103  T* src = I.data()+i*w;
104  T* end = src + w-4;
105  while (src != end) {
106  T sum= (T)(0.0544887*(src[0]+src[4])
107  + 0.2442010*(src[1]+src[3])
108  + 0.4026200*src[2]);
109  *(src+2*w+2)=sum;
110  ++src;
111  }
112  }
113 }
114 
115 namespace Exceptions {
116 
119  namespace Convolution {
122  struct All: public CVD::Exceptions::All {};
123 
126  struct IncompatibleImageSizes : public All {
127  IncompatibleImageSizes(const std::string & function)
128  {
129  what = "Incompatible image sizes in " + function;
130  }
131  };
132 
133 
136  struct OddSizedKernelRequired : public All {
137  OddSizedKernelRequired(const std::string & function)
138  {
139  what = "Odd sized kernel required in " + function;
140  };
141  };
142  }
143 }
144 //void convolveGaussian5_1(BasicImage<byte>& I);
145 
150 template <class T> void convolveWithBox(const BasicImage<T>& I, BasicImage<T>& J, ImageRef hwin)
151 {
152  typedef typename Pixel::traits<T>::wider_type sum_type;
153  if (I.size() != J.size()) {
154  throw Exceptions::Convolution::IncompatibleImageSizes("convolveWithBox");
155  }
156  int w = I.size().x;
157  int h = I.size().y;
158  ImageRef win = 2*hwin+ImageRef(1,1);
159  const double factor = 1.0/(win.x*win.y);
160  std::vector<sum_type> buffer(w*win.y);
161  std::vector<sum_type> sums_v(w);
162  sum_type* sums = &sums_v[0];
163  sum_type* next_row = &buffer[0];
164  sum_type* oldest_row = &buffer[0];
165  zeroPixels(sums, w);
166  const T* input = I.data();
167  T* output = J[hwin.y] - hwin.x;
168  for (int i=0; i<h; i++) {
169  sum_type hsum=sum_type();
170  const T* back = input;
171  int j;
172  for (j=0; j<win.x-1; j++)
173  hsum += input[j];
174  for (; j<w; j++) {
175  hsum += input[j];
176  next_row[j] = hsum;
177  sums[j] += hsum;
178  hsum -= *(back++);
179  }
180  if (i >= win.y-1) {
181  assign_multiple(sums+win.x-1, factor, output+win.x-1, w-win.x+1);
182  differences(sums+win.x-1, oldest_row+win.x-1, sums+win.x-1, w-win.x+1);
183  output += w;
184  oldest_row += w;
185  if (oldest_row == &buffer[0] + w*win.y)
186  oldest_row = &buffer[0];
187  }
188  input += w;
189  next_row += w;
190  if (next_row == &buffer[0] + w*win.y)
191  next_row = &buffer[0];
192  }
193 }
194 
195 template <class T> inline void convolveWithBox(const BasicImage<T>& I, BasicImage<T>& J, int hwin)
196 {
197  convolveWithBox(I, J, ImageRef(hwin,hwin));
198 }
199 
200 template <class T> inline void convolveWithBox(BasicImage<T>& I, int hwin) {
201  convolveWithBox(I,I,hwin);
202 }
203 
204 template <class T> inline void convolveWithBox(BasicImage<T>& I, ImageRef hwin) {
205  convolveWithBox(I,I,hwin);
206 }
207 
208 
209 template <class T, int A, int B, int C> void convolveSymmetric(Image<T>& I)
210  {
211  typedef typename Pixel::traits<T>::wider_type wider;
212  static const wider S = (A+B+C+B+A);
213  int width = I.size().x;
214  int height = I.size().y;
215  T* p = I.data();
216  int i,j;
217  for (i=0; i<height; i++) {
218  wider a = p[0];
219  wider b = p[1];
220  wider c = p[2];
221  wider d = p[3];
222  p[0] = (T)(((c+c)*A+(b+b)*B + a*C) /S);
223  p[1] = (T)(((b+d)*A+(a+c)*B + b*C) /S);
224  for (j=0;j<width-4;j++,p++) {
225  wider e = p[4];
226  p[2] = (T)(((a+e)*A + (b+d)*B + c*C)/S);
227  a = b; b = c; c = d; d = e;
228  }
229  p[2] = (T)(((a+c)*A + (b+d)*B + c*C) /S);
230  p[3] = (T)(((b+b)*A + (c+c)*B + d*C) /S);
231  p += 4;
232  }
233  for (j=0;j<width;j++) {
234  p = I.data()+j;
235  wider a = p[0];
236  wider b = p[width];
237  p[0] = (T)(((p[2*width]+p[2*width])*A+(b+b)*B + a*C) /S);
238  p[width] = (T)(((b+p[width*3])*A+(a+p[2*width])*B + b*C) /S);
239  for (i=0;i<height-4;i++) {
240  wider c = p[2*width];
241  p[2*width] = (T)(((a+p[4*width])*A + (b+p[3*width])*B + c*C)/S);
242  a=b; b=c;
243  p += width;
244  }
245  wider c = p[2*width];
246  p[2*width] = (T)(((a+c)*A + (b+p[width*3])*B + c*C) /S);
247  p[3*width] = (T)(((b+b)*A + (c+c)*B + p[width*3]*C) /S);
248  }
249  }
250 
251  template <class T, int A, int B, int C, int D> void convolveSymmetric(Image<T>& I)
252  {
253  typedef typename Pixel::traits<T>::wider_type wider;
254  static const wider S = (A+B+C+D+C+B+A);
255  int width = I.size().x;
256  int height = I.size().y;
257  T* p = I.data();
258  int i,j;
259  for (i=0; i<height; i++) {
260  wider a = p[0];
261  wider b = p[1];
262  wider c = p[2];
263  wider d = p[3];
264  p[0] = (T)(((d+d)*A + (c+c)*B + (b+b)*C + a*D)/S);
265  p[1] = (T)(((c+p[4])*A + (b+d)*B + (a+c)*C + b*D)/S);
266  p[2] = (T)(((b+p[5])*A + (a+p[4])*B + (b+d)*C + c*D)/S);
267  for (j=0;j<width-6;j++,p++) {
268  d = p[3];
269  p[3] = (T)(((a+p[6])*A + (b+p[5])*B + (c+p[4])*C + d*D)/S);
270  a=b; b=c; c=d;
271  }
272  d = p[3];
273  wider e = p[4];
274  p[3] = (T)(((a+e)*A + (b+p[5])*B + (c+e)*C + d*D)/S);
275  p[4] = (T)(((b+d)*A + (c+e)*B + (d+p[5])*C + e*D)/S);
276  p[5] = (T)(((c+c)*A + (d+d)*B + (e+e)*C + p[5]*D)/S);
277  p += 6;
278  }
279  for (j=0;j<width;j++) {
280  p = I.data()+j;
281  wider a = p[0];
282  wider b = p[width];
283  wider c = p[2*width];
284  wider d = p[3*width];
285  p[0] = (T)(((d+d)*A + (c+c)*B + (b+b)*C + a*D)/S);
286  p[width] = (T)(((c+p[4*width])*A + (b+d)*B + (a+c)*C + b*D)/S);
287  p[2*width] = (T)(((b+p[5*width])*A + (a+p[4*width])*B + (b+d)*C + c*D)/S);
288  for (i=0;i<height-6;i++) {
289  d = p[3*width];
290  p[3*width] = (T)(((a+p[width*6])*A + (b+p[width*5])*B + (c+p[width*4])*C+d*D)/S);
291  a=b; b=c; c=d;
292  p += width;
293  }
294  d = p[3*width];
295  wider e = p[4*width];
296  p[3*width] = (T)(((a+e)*A + (b+p[5*width])*B + (c+e)*C + d*D)/S);
297  p[4*width] = (T)(((b+d)*A + (c+e)*B + (d+p[5*width])*C + e*D)/S);
298  p[5*width] = (T)(((c+c)*A + (d+d)*B + (e+e)*C + p[5*width]*D)/S);
299  }
300  }
301 
302 template <class T, class K> void convolveSeparableSymmetric(Image<T>& I, const std::vector<K>& kernel, K divisor)
303  {
304  typedef typename Pixel::traits<T>::wider_type sum_type;
305  int w = I.size().x;
306  int h = I.size().y;
307  int r = (int)kernel.size()/2;
308  int i,j;
309  int m;
310  double factor = 1.0/divisor;
311  for (j=0;j<w;j++) {
312  T* src = I.data()+j;
313  for (i=0; i<h-2*r; i++,src+=w) {
314  sum_type sum = src[r*w]*kernel[r], v;
315  for (m=0; m<r; m++)
316  sum += (src[m*w] + src[(2*r-m)*w]) * kernel[m];
317  *(src) = static_cast<T>(sum * factor);
318  }
319  }
320  int offset = r*w + r;
321  for (i=h-2*r-1;i>=0;i--) {
322  T* src = I[w];
323  for (j=0;j<w-2*r;j++, src++) {
324  sum_type sum = src[r] * kernel[r], v;
325  for (m=0; m<r; m++)
326  sum += (src[m] + src[2*r-m])*kernel[m];
327  *(src+offset) = static_cast<T>(sum*factor);
328  }
329  }
330  }
331 
332 
333 template <class A, class B> struct GetPixelRowTyped {
334  static inline const B* get(const A* row, int w, B* rowbuf) {
335  std::copy(row, row+w, rowbuf);
336  return rowbuf;
337  }
338 };
339 
340 template <class T> struct GetPixelRowTyped<T,T> {
341  static inline const T* get(const T* row, int , T* ) {
342  return row;
343  }
344 };
345 
346 template <class A, class B> const B* getPixelRowTyped(const A* row, int n, B* rowbuf) {
347  return GetPixelRowTyped<A,B>::get(row,n,rowbuf);
348 }
349 
350 template <class T, class S> struct CastCopy {
351  static inline void cast_copy(const T* from, S* to, int count) {
352  for (int i=0; i<count; i++)
353  to[i] = static_cast<S>(from[i]);
354  }
355 };
356 
357 template <class T> struct CastCopy<T,T> {
358  static inline void cast_copy(const T* from, T* to, int count) {
359  std::copy(from, from+count, to);
360  }
361 };
362 
363 template <class T, class S> inline void cast_copy(const T* from, S* to, int count) { CastCopy<T,S>::cast_copy(from,to,count); }
364 
365 template <class T, int N=-1, int C = Pixel::Component<T>::count> struct ConvolveMiddle {
366  template <class S> static inline T at(const T* input, const S& factor, const S* kernel) { return ConvolveMiddle<T,-1,C>::at(input,factor, kernel, N); }
367 };
368 
369 template <class T, int N> struct ConvolveMiddle<T,N,1> {
370  template <class S> static inline T at(const T* input, const S& factor, const S* kernel) { return ConvolveMiddle<T,N-1>::at(input,factor, kernel) + (input[-N]+input[N])*kernel[N-1]; }
371 };
372 
373 template <class T> struct ConvolveMiddle<T,-1,1> {
374  template <class S> static inline T at(const T* input, const S& factor, const S* kernel, int ksize) {
375  T hsum = *input * factor;
376  for (int k=0; k<ksize; k++)
377  hsum += (input[-k-1] + input[k+1]) * kernel[k];
378  return hsum;
379  }
380 };
381 
382 template <class T, int C> struct ConvolveMiddle<T,-1, C> {
383  template <class S> static inline T at(const T* input, const S& factor, const S* kernel, int ksize) {
384  T hsum = *input * factor;
385  for (int k=0; k<ksize; k++)
386  hsum += (input[-k-1] + input[k+1]) * kernel[k];
387  return hsum;
388  }
389 };
390 
391 template <class T> struct ConvolveMiddle<T,0,1> {
392  template <class S> static inline T at(const T* input, const S& factor, const S* ) { return *input * factor; }
393 };
394 
395 template <class T,class S> inline const T* convolveMiddle(const T* input, const S& factor, const S* kernel, int ksize, int n, T* output) {
396 #define CALL_CM(I) for (int j=0; j<n; ++j, ++input, ++output) { *output = ConvolveMiddle<T,I>::at(input, factor, kernel); } break
397 
398  switch (ksize) {
399  case 0: CALL_CM(0);
400  case 1: CALL_CM(1);
401  case 2: CALL_CM(2);
402  case 3: CALL_CM(3);
403  case 4: CALL_CM(4);
404  case 5: CALL_CM(5);
405  case 6: CALL_CM(6);
406  case 7: CALL_CM(7);
407  case 8: CALL_CM(8);
408  default: for (int j=0; j<n; j++, input++) { *(output++) = ConvolveMiddle<T,-1>::at(input, factor, kernel, ksize); }
409  }
410  return input;
411 #undef CALL_CM
412 }
413 
414 
415 template <class T> inline void convolveGaussian(BasicImage<T>& I, double sigma, double sigmas=3.0)
416 {
417  convolveGaussian(I,I,sigma,sigmas);
418 }
419 
420 template <class T> void convolveGaussian(const BasicImage<T>& I, BasicImage<T>& out, double sigma, double sigmas=3.0)
421 {
422  typedef typename Pixel::traits<typename Pixel::Component<T>::type>::float_type sum_comp_type;
423  typedef typename Pixel::traits<T>::float_type sum_type;
424  assert(out.size() == I.size());
425  int ksize = (int)ceil(sigmas*sigma);
426  //std::cerr << "sigma: " << sigma << " kernel: " << ksize << std::endl;
427  std::vector<sum_comp_type> kernel(ksize);
428  sum_comp_type ksum = sum_comp_type();
429  for (int i=1; i<=ksize; i++)
430  ksum += (kernel[i-1] = static_cast<sum_comp_type>(exp(-i*i/(2*sigma*sigma))));
431  for (int i=0; i<ksize; i++)
432  kernel[i] /= (2*ksum+1);
433  double factor = 1.0/(2*ksum+1);
434  int w = I.size().x;
435  int h = I.size().y;
436  int swin = 2*ksize;
437 
438  AlignedMem<sum_type,16> buffer(w*(swin+1));
439  AlignedMem<sum_type,16> aligned_rowbuf(w);
440  AlignedMem<sum_type,16> aligned_outbuf(w);
441 
442  sum_type* rowbuf = aligned_rowbuf.data();
443  sum_type* outbuf = aligned_outbuf.data();
444 
445  std::vector<sum_type*> rows(swin+1);
446  for (int k=0;k<swin+1;k++)
447  rows[k] = buffer.data() + k*w;
448 
449  T* output = out.data();
450  for (int i=0; i<h; i++) {
451  sum_type* next_row = rows[swin];
452  const sum_type* input = getPixelRowTyped(I[i], w, rowbuf);
453  // beginning of row
454  for (int j=0; j<ksize; j++) {
455  sum_type hsum = static_cast<sum_type>(input[j] * factor);
456  for (int k=0; k<ksize; k++)
457  hsum += (input[std::max(j-k-1,0)] + input[j+k+1]) * kernel[k];
458  next_row[j] = hsum;
459  }
460  // middle of row
461  input += ksize;
462  input = convolveMiddle<sum_type, sum_comp_type>(input, static_cast<sum_comp_type>(factor), &kernel.front(), ksize, w-swin, next_row+ksize);
463  // end of row
464  for (int j=w-ksize; j<w; j++, input++) {
465  sum_type hsum = static_cast<sum_type>(*input * factor);
466  const int room = w-j;
467  for (int k=0; k<ksize; k++) {
468  hsum += (input[-k-1] + input[std::min(k+1,room-1)]) * kernel[k];
469  }
470  next_row[j] = hsum;
471  }
472  // vertical
473  if (i >= swin) {
474  const sum_type* middle_row = rows[ksize];
475  assign_multiple(middle_row, factor, outbuf, w);
476  for (int k=0; k<ksize; k++) {
477  const sum_comp_type m = kernel[k];
478  const sum_type* row1 = rows[ksize-k-1];
479  const sum_type* row2 = rows[ksize+k+1];
480  add_multiple_of_sum(row1, row2, m, outbuf, w);
481  }
482  cast_copy(outbuf, output, w);
483  output += w;
484  if (i == h-1) {
485  for (int r=0; r<ksize; r++) {
486  const sum_type* middle_row = rows[ksize+r+1];
487  assign_multiple(middle_row, factor, outbuf, w);
488  for (int k=0; k<ksize; k++) {
489  const sum_comp_type m = kernel[k];
490  const sum_type* row1 = rows[ksize+r-k];
491  const sum_type* row2 = rows[std::min(ksize+r+k+2, swin)];
492  add_multiple_of_sum(row1, row2, m, outbuf, w);
493  }
494  cast_copy(outbuf, output, w);
495  output += w;
496  }
497  }
498  } else if (i == swin-1) {
499  for (int r=0; r<ksize; r++) {
500  const sum_type* middle_row = rows[r+1];
501  assign_multiple(middle_row, factor, outbuf, w);
502  for (int k=0; k<ksize; k++) {
503  const sum_comp_type m = kernel[k];
504  const sum_type* row1 = rows[std::max(r-k-1,0)+1];
505  const sum_type* row2 = rows[r+k+2];
506  add_multiple_of_sum(row1, row2, m, outbuf, w);
507  }
508  cast_copy(outbuf, output, w);
509  output += w;
510  }
511  }
512 
513  sum_type* tmp = rows[0];
514  for (int r=0;r<swin; r++)
515  rows[r] = rows[r+1];
516  rows[swin] = tmp;
517  }
518 }
519 
520 void compute_van_vliet_b(double sigma, double b[]);
521 void compute_triggs_M(const double b[], double M[][3]);
522 void van_vliet_blur(const double b[], const SubImage<float> in, SubImage<float> out);
523 
524 void convolveGaussian(const BasicImage<float>& I, BasicImage<float>& out, double sigma, double sigmas=3.0);
525 void convolveGaussian_fir(const BasicImage<float>& I, BasicImage<float>& out, double sigma, double sigmas=3.0);
526 
527 template <class T, class O, class K> void convolve_gaussian_3(const BasicImage<T>& I, BasicImage<O>& out, K k1, K k2)
528 {
529  assert(I.size() == out.size());
530  const T* a=I.data();
531  const int w = I.size().x;
532  O* o = out.data()+w+1;
533  int total = I.totalsize() - 2*w-2;
534  const double cross = k1*k2;
535  k1 *= k1;
536  k2 *= k2;
537  while (total--) {
538  const double sum = k1*(a[0] + a[2] + a[w*2] + a[w*2+2]) + cross*(a[1] + a[w*2+1] + a[w] + a[w+2]) + k2*a[w+1];
539  *o++ = Pixel::scalar_convert<O,T,double>(sum);
540  ++a;
541  }
542 }
543 
544 } // namespace CVD
545 
546 #endif
static T at(const T *input, const S &factor, const S *kernel)
Definition: convolution.h:366
void cast_copy(const T *from, S *to, int count)
Definition: convolution.h:363
void convolveSeparableSymmetric(Image< T > &I, const std::vector< K > &kernel, K divisor)
Definition: convolution.h:302
int totalsize() const
What is the total number of elements in the image (i.e. size().x * size().y), including padding...
Definition: image.h:382
int y
The y co-ordinate.
Definition: image_ref.h:180
int x
The x co-ordinate.
Definition: image_ref.h:179
#define CALL_CM(I)
void convolveGaussian_fir(const BasicImage< float > &I, BasicImage< float > &out, double sigma, double sigmas=3.0)
T gaussianKernel(std::vector< T > &k, T maxval, double stddev)
Definition: convolution.h:47
void convolveGaussian(BasicImage< T > &I, double sigma, double sigmas=3.0)
Definition: convolution.h:415
const T * convolveMiddle(const T *input, const S &factor, const S *kernel, int ksize, int n, T *output)
Definition: convolution.h:395
static T at(const T *input, const S &factor, const S *kernel, int ksize)
Definition: convolution.h:383
Definition: abs.h:24
IncompatibleImageSizes(const std::string &function)
Definition: convolution.h:127
static void cast_copy(const T *from, S *to, int count)
Definition: convolution.h:351
void copy(const BasicImage< S > &in, BasicImage< T > &out, ImageRef size=ImageRef(-1,-1), ImageRef begin=ImageRef(), ImageRef dst=ImageRef())
Definition: utility.h:26
OddSizedKernelRequired(const std::string &function)
Definition: convolution.h:137
void convolveGaussian5_1(Image< byte > &I)
Definition: convolution.cpp:43
void zeroPixels(T *pixels, int count)
Definition: utility.h:85
static void cast_copy(const T *from, T *to, int count)
Definition: convolution.h:358
Matrix< R, C, P > exp(const Matrix< R, C, P, B > &m)
Definition: helpers.h:284
ImageRef size() const
What is the size of this image?
Definition: image.h:370
static T at(const T *input, const S &factor, const S *kernel, int ksize)
Definition: convolution.h:374
const T * data() const
Returns the raw image data.
Definition: image.h:326
T scaleKernel(const std::vector< S > &k, std::vector< T > &scaled, T maxval)
Definition: convolution.h:72
void convolve_gaussian_3(const BasicImage< T > &I, BasicImage< O > &out, K k1, K k2)
Definition: convolution.h:527
void add_multiple_of_sum(const A *a, const A *b, const C &c, B *out, size_t count)
Definition: utility.h:137
void convolveSymmetric(Image< T > &I)
Definition: convolution.h:209
void assign_multiple(const A *a, const B &c, C *out, size_t count)
Definition: utility.h:146
void compute_triggs_M(const double b[], double M[][3])
static T at(const T *input, const S &factor, const S *kernel)
Definition: convolution.h:370
void convolveWithBox(const BasicImage< T > &I, BasicImage< T > &J, ImageRef hwin)
Definition: convolution.h:150
std::string what
The error message.
Definition: exceptions.h:38
const B * getPixelRowTyped(const A *row, int n, B *rowbuf)
Definition: convolution.h:346
static T at(const T *input, const S &factor, const S *)
Definition: convolution.h:392
void differences(const A *a, const A *b, B *diff, size_t count)
Definition: utility.h:128
void compute_van_vliet_b(double sigma, double b[])
static const B * get(const A *row, int w, B *rowbuf)
Definition: convolution.h:334
void van_vliet_blur(const double b[], const CVD::SubImage< float > in, CVD::SubImage< float > out)