A Survey of Gaussian Convolution Algorithms
gaussian_demo.c
Go to the documentation of this file.
1 
20 #include <ctype.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <math.h>
25 #include "imageio.h"
26 #include "gaussian_conv_fir.h"
27 #include "gaussian_conv_dct.h"
28 #include "gaussian_conv_box.h"
29 #include "gaussian_conv_ebox.h"
30 #include "gaussian_conv_sii.h"
31 #include "gaussian_conv_am.h"
32 #include "gaussian_conv_deriche.h"
33 #include "gaussian_conv_vyv.h"
34 
37 {
38  puts("Gaussian convolution demo, P. Getreuer 2013");
39 #ifdef NUM_SINGLE
40  puts("Single-precision computation");
41 #else
42  puts("Double-precision computation");
43 #endif
44  puts("\nSyntax: gaussian_conv_demo [options] <input> <output>\n");
45  puts("Only " READIMAGE_FORMATS_SUPPORTED " images are supported.\n");
46  puts("Options:");
47  puts(" -a <algo> algorithm to use, choices are");
48  puts(" fir FIR approximation, tol = kernel accuracy");
49  puts(" dct DCT-based convolution");
50  puts(" box box filtering, K = # passes");
51  puts(" sii stacked integral images, K = # boxes");
52  puts(" am Alvarez-Mazorra using regression on q,");
53  puts(" K = # passes, tol = boundary accuracy");
54  puts(" deriche Deriche recursive filtering,");
55  puts(" K = order, tol = boundary accuracy");
56  puts(" vyv Vliet-Young-Verbeek recursive filtering,");
57  puts(" K = order, tol = boundary accuracy");
58  puts(" -s <number> sigma, standard deviation of the Gaussian");
59  puts(" -K <number> specifies number of steps (box, sii, am)");
60  puts(" -t <number> accuracy tolerance (fir, am, deriche, yv)\n");
61 }
62 
64 typedef struct
65 {
67  const char *input_file;
69  const char *output_file;
70 
72  const char *algo;
74  double sigma;
76  int K;
78  double tol;
80 
81 int parse_params(program_params *param, int argc, char **argv);
82 int is_grayscale(const num *rgb_image, long num_pixels);
83 
84 void normalize(num *output_image, long num_pixels) {
85  num min_value = output_image[0];
86  num max_value = output_image[0];
87  num scale;
88  long i;
89  for (i = 1; i < num_pixels; ++i) {
90  if (output_image[i] < min_value) {
91  min_value = output_image[i];
92  } else if (output_image[i] > max_value) {
93  max_value = output_image[i];
94  }
95  }
96  scale = 1.0 / (max_value - min_value);
97  for (i = 0; i < num_pixels; ++i) {
98  output_image[i] = (output_image[i] - min_value) * scale;
99  }
100 }
101 
102 int main(int argc, char **argv)
103 {
104  program_params param;
105  num *input_image = NULL;
106  num *output_image = NULL;
107  unsigned long time_start;
108  long num_pixels;
109  int width, height, num_channels, success = 0;
110 
111  if (!parse_params(&param, argc, argv))
112  return 1;
113 
114  /* Read the input image. */
115  if (!(input_image = (num *)read_image(&width, &height, param.input_file,
116  IMAGEIO_NUM | IMAGEIO_PLANAR | IMAGEIO_RGB)))
117  goto fail;
118 
119  num_pixels = ((long)width) * ((long)height);
120  num_channels = is_grayscale(input_image, num_pixels) ? 1 : 3;
121 
122  /* Allocate the output image. */
123  if (!(output_image = (num *)malloc(sizeof(num)
124  * num_channels * num_pixels)))
125  goto fail;
126 
127  printf("Convolving %dx%d %s image with Gaussian, sigma=%g\n",
128  width, height, (num_channels == 3 ? "RGB" : "gray"), param.sigma);
129  time_start = millisecond_timer();
130 
131  if (!strcmp(param.algo, "fir"))
132  { /* FIR convolution. */
133  num *buffer = NULL;
134  fir_coeffs c;
135 
136  printf("FIR convolution, tol=%g\n", param.tol);
137 
138  if (!(buffer = (num *)malloc(sizeof(num) * width)))
139  goto fail;
140  if (!(fir_precomp(&c, param.sigma, param.tol)))
141  {
142  free(buffer);
143  goto fail;
144  }
145 
146  fir_gaussian_conv_image(c, output_image, buffer, input_image,
147  width, height, num_channels);
148  fir_free(&c);
149  free(buffer);
150  }
151  else if (!strcmp(param.algo, "dct"))
152  { /* DCT-based convolution. */
153  dct_coeffs c;
154 
155  printf("DCT-based convolution\n");
156 
157  if (!(dct_precomp_image(&c, output_image, input_image,
158  width, height, num_channels, param.sigma)))
159  goto fail;
160 
162  dct_free(&c);
163  }
164  else if (!strcmp(param.algo, "box"))
165  { /* Basic box filtering. */
166  num *buffer = NULL;
167 
168  printf("Box filtering, K=%d passes\n",
169  param.K);
170 
171  if (!(buffer = (num *)malloc(sizeof(num) *
172  ((width >= height) ? width : height))))
173  {
174  fprintf(stderr, "Error: Out of memory\n");
175  goto fail;
176  }
177 
178  box_gaussian_conv_image(output_image, buffer, input_image,
179  width, height, num_channels, param.sigma, param.K);
180  free(buffer);
181  }
182  else if (!strcmp(param.algo, "ebox"))
183  { /* Extended box filtering. */
184  num *buffer = NULL;
185  ebox_coeffs c;
186 
187  printf("Extended box filtering, K=%d passes\n",
188  param.K);
189 
190  if (!(buffer = (num *)malloc(sizeof(num) *
191  ((width >= height) ? width : height))))
192  {
193  fprintf(stderr, "Error: Out of memory\n");
194  goto fail;
195  }
196 
197  ebox_precomp(&c, param.sigma, param.K);
198  ebox_gaussian_conv_image(c, output_image, buffer, input_image,
199  width, height, num_channels);
200  free(buffer);
201  }
202  else if (!strcmp(param.algo, "sii"))
203  { /* Stacked integral images. */
204  num *buffer = NULL;
205  sii_coeffs c;
206 
207  if (!SII_VALID_K(param.K))
208  {
209  fprintf(stderr, "Error: K=%d is invalid for SII\n", param.K);
210  goto fail;
211  }
212 
213  printf("Stacked integral images, K=%d boxes\n", param.K);
214  sii_precomp(&c, param.sigma, param.K);
215 
216  if (!(buffer = (num *)malloc(sizeof(num) * sii_buffer_size(c,
217  ((width >= height) ? width : height)))))
218  goto fail;
219 
220  sii_gaussian_conv_image(c, output_image, buffer, input_image,
221  width, height, num_channels);
222  free(buffer);
223  }
224  else if (!strcmp(param.algo, "am"))
225  { /* Alvarez-Mazorra recursive filtering (using regression on q). */
226  printf("Alvarez-Mazorra recursive filtering, K=%d passes,"
227  " tol=%g left boundary accuracy\n", param.K, param.tol);
228  am_gaussian_conv_image(output_image, input_image,
229  width, height, num_channels,
230  param.sigma, param.K, param.tol, 1);
231  }
232  else if (!strcmp(param.algo, "deriche"))
233  { /* Deriche recursive filtering. */
234  num *buffer = NULL;
235  deriche_coeffs c;
236 
237  if (!DERICHE_VALID_K(param.K))
238  {
239  fprintf(stderr, "Error: K=%d is invalid for Deriche\n", param.K);
240  goto fail;
241  }
242 
243  printf("Deriche recursive filtering,"
244  " K=%d, tol=%g boundary accuracy\n", param.K, param.tol);
245 
246  if (!DERICHE_VALID_K(param.K)
247  || !(buffer = (num *)malloc(sizeof(num) * 2 *
248  ((width >= height) ? width : height))))
249  {
250  fprintf(stderr, "Error: Out of memory\n");
251  goto fail;
252  }
253 
254  deriche_precomp(&c, param.sigma, param.K, param.tol);
255  deriche_gaussian_conv_image(c, output_image, buffer, input_image,
256  width, height, num_channels);
257  free(buffer);
258  }
259  else if (!strcmp(param.algo, "vyv"))
260  { /* Vliet-Young-Verbeek recursive filtering. */
261  vyv_coeffs c;
262 
263  if (!VYV_VALID_K(param.K))
264  {
265  fprintf(stderr, "Error: K=%d is invalid for VYV\n", param.K);
266  goto fail;
267  }
268 
269  printf("Vliet-Young-Verbeek recursive filtering,"
270  " K=%d, tol=%g left boundary accuracy\n", param.K, param.tol);
271  vyv_precomp(&c, param.sigma, param.K, param.tol);
272  vyv_gaussian_conv_image(c, output_image, input_image,
273  width, height, num_channels);
274  }
275  else
276  {
277  fprintf(stderr, "Unknown method \"%s\".\n", param.algo);
278  goto fail;
279  }
280 
281  printf("CPU Time: %.3f s\n", 0.001f*(millisecond_timer() - time_start));
282 
283  normalize(output_image, width * height);
284 
285  /* Write output image */
286  if (!write_image(output_image, width, height, param.output_file,
287  IMAGEIO_NUM | IMAGEIO_PLANAR
288  | ((num_channels == 1) ? IMAGEIO_GRAYSCALE : IMAGEIO_RGB), 95))
289  goto fail;
290 
291  success = 1;
292 fail:
293  if (output_image)
294  free(output_image);
295  if (input_image)
296  free(input_image);
297  return !success;
298 }
299 
306 int is_grayscale(const num *rgb_image, long num_pixels)
307 {
308  const num *red = rgb_image;
309  const num *green = rgb_image + num_pixels;
310  const num *blue = rgb_image + 2 * num_pixels;
311  long i;
312 
313  for (i = 0; i < num_pixels; ++i)
314  if (red[i] != green[i] || red[i] != blue[i])
315  return 0; /* Not a grayscale image. */
316 
317  return 1; /* This is a grayscale image. */
318 }
319 
321 int parse_params(program_params *param, int argc, char **argv)
322 {
323  static const char *default_output_file = (const char *)"out.bmp";
324  static const char *default_algo = (const char *)"exact";
325  char *option_string;
326  char option_char;
327  int i;
328 
329  if (argc < 2)
330  {
331  print_usage();
332  return 0;
333  }
334 
335  /* Set parameter defaults. */
336  param->input_file = NULL;
337  param->output_file = default_output_file;
338  param->sigma = 5;
339  param->algo = default_algo;
340  param->K = 3;
341  param->tol = 1e-2;
342 
343  for (i = 1; i < argc;)
344  {
345  if (argv[i] && argv[i][0] == '-')
346  {
347  if ((option_char = argv[i][1]) == 0)
348  {
349  fprintf(stderr, "Invalid parameter format.\n");
350  return 0;
351  }
352 
353  if (argv[i][2])
354  option_string = &argv[i][2];
355  else if (++i < argc)
356  option_string = argv[i];
357  else
358  {
359  fprintf(stderr, "Invalid parameter format.\n");
360  return 0;
361  }
362 
363  switch (option_char)
364  {
365  case 'a': /* Read algorithm. */
366  param->algo = option_string;
367  break;
368  case 's': /* Read sigma parameter. */
369  param->sigma = atof(option_string);
370 
371  if (param->sigma < 0)
372  {
373  fprintf(stderr, "sigma must be positive.\n");
374  return 0;
375  }
376  break;
377  case 'K': /* Read number of steps. */
378  param->K = atoi(option_string);
379 
380  if (param->K < 0)
381  {
382  fprintf(stderr, "K must be positive.\n");
383  return 0;
384  }
385  break;
386  case 't': /* Read tolerance. */
387  param->tol = atof(option_string);
388 
389  if (param->tol < 0)
390  {
391  fprintf(stderr, "Tolerance must be positive.\n");
392  return 0;
393  }
394  break;
395  case '-':
396  print_usage();
397  return 0;
398  default:
399  if (isprint(option_char))
400  fprintf(stderr, "Unknown option \"-%c\".\n", option_char);
401  else
402  fprintf(stderr, "Unknown option.\n");
403 
404  return 0;
405  }
406 
407  i++;
408  }
409  else
410  {
411  if (!param->input_file)
412  param->input_file = argv[i];
413  else
414  param->output_file = argv[i];
415 
416  i++;
417  }
418  }
419 
420  if (!param->input_file)
421  {
422  print_usage();
423  return 0;
424  }
425 
426  return 1;
427 }