A Survey of Gaussian Convolution Algorithms
gaussian_conv_am.c
Go to the documentation of this file.
1 
20 #include <assert.h>
21 #include <math.h>
22 #include "filter_util.h"
23 #include "gaussian_conv_am.h"
24 
38 static num am_left_boundary(const num *data, long N, long stride,
39  num nu, long num_terms)
40 {
41  num h = 1, accum = data[0];
42  long m;
43 
44  for (m = 1; m < num_terms; ++m)
45  {
46  h *= nu;
47  accum += h * data[stride * extension(N, -m)];
48  }
49 
50  return accum;
51 }
52 
81 void am_gaussian_conv(num *dest, const num *src, long N, long stride,
82  double sigma, int K, num tol, int use_adjusted_q)
83 {
84  const long stride_N = stride * N;
85  double q, lambda, dnu;
86  num nu, scale;
87  long i, num_terms;
88  int pass;
89 
90  assert(dest && src && N > 0 && stride != 0 && sigma > 0
91  && K > 0 && tol > 0);
92 
93  if (use_adjusted_q) /* Use a regression on q for improved accuracy. */
94  q = sigma * (1.0 + (0.3165 * K + 0.5695)
95  / ((K + 0.7818) * (K + 0.7818)));
96  else /* Use q = sigma as in the original A-M method. */
97  q = sigma;
98 
99  /* Precompute the filter coefficient nu. */
100  lambda = (q * q) / (2.0 * K);
101  dnu = (1.0 + 2.0*lambda - sqrt(1.0 + 4.0*lambda))/(2.0*lambda);
102  nu = (num)dnu;
103  /* For handling the left boundary, determine the number of terms needed to
104  approximate the sum with accuracy tol. */
105  num_terms = (long)ceil(log((1.0 - dnu)*tol) / log(dnu));
106  /* Precompute the constant scale factor. */
107  scale = (num)(pow(dnu / lambda, K));
108 
109  /* Copy src to dest and multiply by the constant scale factor. */
110  for (i = 0; i < stride_N; i += stride)
111  dest[i] = src[i] * scale;
112 
113  /* Perform K passes of filtering. */
114  for (pass = 0; pass < K; ++pass)
115  {
116  /* Initialize the recursive filter on the left boundary. */
117  dest[0] = am_left_boundary(dest, N, stride, nu, num_terms);
118 
119  /* This loop applies the causal filter, implementing the pseudocode
120 
121  For n = 1, ..., N - 1
122  dest(n) = dest(n) + nu dest(n - 1)
123 
124  Variable i = stride * n is the offset to the nth sample. */
125  for (i = stride; i < stride_N; i += stride)
126  dest[i] += nu * dest[i - stride];
127 
128  /* Handle the right boundary. */
129  i -= stride;
130  dest[i] /= (1 - nu);
131 
132  /* Similarly, this loop applies the anticausal filter as
133 
134  For n = N - 1, ..., 1
135  dest(n - 1) = dest(n - 1) + nu dest(n) */
136  for (; i > 0; i -= stride)
137  dest[i - stride] += nu * dest[i];
138  }
139 
140  return;
141 }
142 
159 void am_gaussian_conv_image(num *dest, const num *src,
160  int width, int height, int num_channels,
161  num sigma, int K, num tol, int use_adjusted_q)
162 {
163  const long num_pixels = ((long)width) * ((long)height);
164  int x, y, channel;
165 
166  assert(dest && src && num_pixels > 0 && sigma > 0
167  && K > 0 && tol > 0);
168 
169  /* Loop over the image channels. */
170  for (channel = 0; channel < num_channels; ++channel)
171  {
172  num *dest_y = dest;
173  const num *src_y = src;
174 
175  /* Filter each row of the channel. */
176  for (y = 0; y < height; ++y)
177  {
178  am_gaussian_conv(dest_y, src_y, width, 1,
179  sigma, K, tol, use_adjusted_q);
180  dest_y += width;
181  src_y += width;
182  }
183 
184  /* Filter each column of the channel. */
185  for (x = 0; x < width; ++x)
186  am_gaussian_conv(dest + x, dest + x, height, width,
187  sigma, K, tol, use_adjusted_q);
188 
189  dest += num_pixels;
190  src += num_pixels;
191  }
192 
193  return;
194 }