A Survey of Gaussian Convolution Algorithms
imdiff.c
Go to the documentation of this file.
1 
38 #include <ctype.h>
39 #include <math.h>
40 #include <string.h>
41 #include "imageio.h"
42 
44 #define DISPLAY_SCALING 255
45 
47 typedef enum {DEFAULT_METRICS, MAX_METRIC, MSE_METRIC, RMSE_METRIC,
48  PSNR_METRIC} metric_t;
49 
51 typedef struct
52 {
54  char *file_a;
56  char *file_b;
64  int pad;
65 
69  float D;
71 
72 
73 void make_difference_image(float *A, const float *B,
74  int width, int height, int num_channels, float D);
75 void basic_metrics(float *max_diff, float *mse, const float *A, const float *B,
76  int width, int height, int num_channels, int pad);
77 int parse_params(programparams *param, int argc, char *argv[]);
78 
81 {
82  puts("Image difference calculator, P. Getreuer 2010-2011, 2013\n");
83  puts("Syntax: imdiff [options] <exact file> <distorted file>\n"
84  "Only " READIMAGE_FORMATS_SUPPORTED " images are supported.\n");
85  puts("Options:");
86  puts(" -m <metric> metric to use for comparison, choices are");
87  puts(" max Max absolute difference, max_n |A_n - B_n|");
88  puts(" mse Mean squared error, 1/N sum |A_n - B_n|^2");
89  puts(" rmse Root mean squared error, (MSE)^1/2");
90  puts(" psnr Peak signal-to-noise ratio, -10 log10(MSE/255^2)");
91  puts(" -s Compute metric separately for each channel");
92  puts(" -p <pad> Remove a margin of <pad> pixels before comparison");
93  puts(" -D <number> D parameter for difference image\n");
94 #ifdef USE_LIBJPEG
95  puts(" -q <number> Quality for saving JPEG images (0 to 100)\n");
96 #endif
97  puts("Alternatively, a difference image is generated by the syntax\n"
98  " imdiff [-D <number>] <exact file> <distorted file> <output file>\n");
99  puts("The difference image is computed as\n"
100  " D_n = 255/D (A_n - B_n) + 255/2.\n"
101  "Values outside of the range [0,255] are saturated.\n");
102  puts("Example:\n"
103 #ifdef USE_LIBPNG
104  " imdiff -mpsnr frog-exact.png frog-4x.png");
105 #else
106  " imdiff -mpsnr frog-exact.bmp frog-4x.bmp");
107 #endif
108 }
109 
110 int main(int argc, char *argv[])
111 {
112  struct
113  {
114  float *data;
115  int width;
116  int height;
117  } A = {NULL, 0, 0}, B = {NULL, 0, 0};
118  programparams param;
119  float max_diff, max_diff_c[3], mse, mse_c[3];
120  int channel, status = 1;
121 
122  if(!parse_params(&param, argc, argv))
123  return 0;
124 
125  /* Read the exact image. */
126  if(!(A.data = (float *)read_image(&A.width, &A.height, param.file_a,
127  IMAGEIO_FLOAT | IMAGEIO_RGB | IMAGEIO_PLANAR)))
128  goto fail;
129 
130  /* Read the distorted image. */
131  if(!(B.data = (float *)read_image(&B.width, &B.height, param.file_b,
132  IMAGEIO_FLOAT | IMAGEIO_RGB | IMAGEIO_PLANAR)))
133  goto fail;
134 
135  if(A.width != B.width || A.height != B.height)
136  {
137  fprintf(stderr, "Image sizes don't match, %dx%d vs. %dx%d.\n",
138  A.width, A.height, B.width, B.height);
139  goto fail;
140  }
141  else if(A.width <= 2 * param.pad || A.height <= 2 * param.pad)
142  {
143  fprintf(stderr,
144  "Removal of %d-pixel padding removes entire %dx%d image.\n",
145  param.pad, A.width, A.height);
146  goto fail;
147  }
148 
149  if(param.difference_file)
150  {
151  make_difference_image(A.data, B.data, A.width, A.height, 3, param.D);
152 
153  if(!(write_image(A.data, A.width, A.height, param.difference_file,
154  IMAGEIO_FLOAT | IMAGEIO_RGB | IMAGEIO_PLANAR, param.jpeg_quality)))
155  goto fail;
156  }
157  else
158  {
159  max_diff = 0.0f;
160  mse = 0.0f;
161 
162  for(channel = 0; channel < 3; channel++)
163  {
164  basic_metrics(&max_diff_c[channel], &mse_c[channel],
165  A.data + channel*A.width*A.height,
166  B.data + channel*B.width*B.height,
167  A.width, A.height, 1, param.pad);
168 
169  if(max_diff_c[channel] > max_diff)
170  max_diff = max_diff_c[channel];
171 
172  mse += mse_c[channel];
173  }
174 
175  mse /= 3;
176 
177  switch(param.metric)
178  {
179  case DEFAULT_METRICS:
180  if(!param.separate_channels)
181  {
182  printf("Maximum absolute difference: %g\n",
183  DISPLAY_SCALING * max_diff);
184  printf("Root mean squared error: %.4f\n",
185  DISPLAY_SCALING * sqrt(mse));
186  printf("Peak signal-to-noise ratio: %.4f\n",
187  -10 * log10(mse));
188  }
189  else
190  {
191  printf("Maximum absolute difference: %g %g %g\n",
192  DISPLAY_SCALING * max_diff_c[0],
193  DISPLAY_SCALING * max_diff_c[1],
194  DISPLAY_SCALING * max_diff_c[2]);
195  printf("Root mean squared error: %.4f %.4f %.4f\n",
196  DISPLAY_SCALING * sqrt(mse_c[0]),
197  DISPLAY_SCALING * sqrt(mse_c[1]),
198  DISPLAY_SCALING * sqrt(mse_c[2]));
199  printf("Peak signal-to-noise ratio: %.4f %.4f %.4f\n",
200  -10 * log10(mse_c[0]),
201  -10 * log10(mse_c[1]),
202  -10 * log10(mse_c[2]));
203  }
204  break;
205  case MAX_METRIC:
206  if(!param.separate_channels)
207  printf("%g\n", DISPLAY_SCALING*max_diff);
208  else
209  printf("%g %g %g\n",
210  DISPLAY_SCALING * max_diff_c[0],
211  DISPLAY_SCALING * max_diff_c[1],
212  DISPLAY_SCALING * max_diff_c[2]);
213  break;
214  case MSE_METRIC:
215  if(!param.separate_channels)
216  printf("%.4f\n", DISPLAY_SCALING * DISPLAY_SCALING * mse);
217  else
218  printf("%.4f %.4f %.4f\n",
219  DISPLAY_SCALING * DISPLAY_SCALING * mse_c[0],
220  DISPLAY_SCALING * DISPLAY_SCALING * mse_c[1],
221  DISPLAY_SCALING * DISPLAY_SCALING * mse_c[2]);
222  break;
223  case RMSE_METRIC:
224  if(!param.separate_channels)
225  printf("%.4f\n", DISPLAY_SCALING * sqrt(mse));
226  else
227  printf("%.4f %.4f %.4f\n",
228  DISPLAY_SCALING * sqrt(mse_c[0]),
229  DISPLAY_SCALING * sqrt(mse_c[1]),
230  DISPLAY_SCALING * sqrt(mse_c[2]));
231  break;
232  case PSNR_METRIC:
233  if(!param.separate_channels)
234  printf("%.4f\n", -10 * log10(mse));
235  else
236  printf("%.4f %.4f %.4f\n",
237  -10 * log10(mse_c[0]),
238  -10 * log10(mse_c[1]),
239  -10 * log10(mse_c[2]));
240  break;
241  }
242  }
243 
244  status = 0;
245 fail:
246  if(B.data)
247  free(B.data);
248  if(A.data)
249  free(A.data);
250  return status;
251 }
252 
254 void make_difference_image(float *A, const float *B,
255  int width, int height, int num_channels, float D)
256 {
257  const int num_el = num_channels * width * height;
258  int n;
259 
260  D /= 255;
261 
262  for(n = 0; n < num_el; n++)
263  A[n] = (A[n] - B[n]) / D + 0.5f;
264 }
265 
267 void basic_metrics(float *max_diff, float *mse, const float *A, const float *B,
268  int width, int height, int num_channels, int pad)
269 {
270  float diff, cur_max = 0;
271  double accum_mse = 0;
272  int x, y, channel, n;
273 
274  for(channel = 0; channel < num_channels; channel++)
275  for(y = pad; y < height - pad; y++)
276  for(x = pad; x < width - pad; x++)
277  {
278  n = x + width * (y + height * channel);
279  diff = (float)fabs(A[n] - B[n]);
280 
281  if(cur_max < diff)
282  cur_max = diff;
283 
284  accum_mse += diff*diff;
285  }
286 
287  *max_diff = cur_max;
288  *mse = (float)(
289  accum_mse / (num_channels * (width - 2 * pad) * (height - 2 * pad)));
290 }
291 
293 int parse_params(programparams *param, int argc, char *argv[])
294 {
295  char *option_string;
296  char option_char;
297  int i;
298 
299  if(argc < 2)
300  {
302  return 0;
303  }
304 
305  /* Set parameter defaults. */
306  param->file_a = NULL;
307  param->file_b = NULL;
308  param->metric = DEFAULT_METRICS;
309  param->separate_channels = 0;
310 
311  param->pad = 0;
312  param->difference_file = NULL;
313  param->jpeg_quality = 95;
314  param->D = 20;
315 
316  for(i = 1; i < argc;)
317  {
318  if(argv[i] && argv[i][0] == '-')
319  {
320  if((option_char = argv[i][1]) == 0)
321  {
322  fprintf(stderr, "Invalid parameter format.\n");
323  return 0;
324  }
325 
326  if(argv[i][2])
327  option_string = &argv[i][2];
328  else if(++i < argc)
329  option_string = argv[i];
330  else
331  {
332  fprintf(stderr, "Invalid parameter format.\n");
333  return 0;
334  }
335 
336  switch(option_char)
337  {
338  case 'p':
339  param->pad = atoi(option_string);
340 
341  if(param->pad < 0)
342  {
343  fprintf(stderr, "pad must be nonnegative.\n");
344  return 0;
345  }
346  break;
347  case 's':
348  param->separate_channels = 1;
349  i--;
350  break;
351  case 'D':
352  param->D = (float)atof(option_string);
353 
354  if(param->D <= 0)
355  {
356  fprintf(stderr, "D must be positive.\n");
357  return 0;
358  }
359  break;
360  case 'm':
361  if(!strcmp(option_string, "max"))
362  param->metric = MAX_METRIC;
363  else if(!strcmp(option_string, "mse"))
364  param->metric = MSE_METRIC;
365  else if(!strcmp(option_string, "rmse"))
366  param->metric = RMSE_METRIC;
367  else if(!strcmp(option_string, "psnr"))
368  param->metric = PSNR_METRIC;
369  else
370  fprintf(stderr, "Unknown metric.\n");
371  break;
372 
373 #ifdef USE_LIBJPEG
374  case 'q':
375  param->jpeg_quality = atoi(option_string);
376 
377  if(param->jpeg_quality <= 0 || param->jpeg_quality > 100)
378  {
379  fprintf(stderr,
380  "JPEG quality must be between 0 and 100.\n");
381  return 0;
382  }
383  break;
384 #endif
385  case '-':
387  return 0;
388  default:
389  if(isprint(option_char))
390  fprintf(stderr, "Unknown option \"-%c\".\n", option_char);
391  else
392  fprintf(stderr, "Unknown option.\n");
393 
394  return 0;
395  }
396 
397  i++;
398  }
399  else
400  {
401  if(!param->file_a)
402  param->file_a = argv[i];
403  else if(!param->file_b)
404  param->file_b = argv[i];
405  else
406  param->difference_file = argv[i];
407 
408  i++;
409  }
410  }
411 
412  if(!param->file_a || !param->file_b)
413  {
415  return 0;
416  }
417 
418  return 1;
419 }