Rudin-Osher-Fatemi Total Variation Denoising using Split Bregman
tvdenoise.c
Go to the documentation of this file.
1 
21 #include <math.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include "num.h"
26 #include "tvreg.h"
27 #include "imageio.h"
28 
30 #define DISPLAY_SCALING 255
31 
33 #define LAMBDA_TUNE_ITERATIONS 5
34 
36 #define JPEGQUALITY 95
37 
38 #ifdef NUM_SINGLE
39 #define IMAGEIO_NUM (IMAGEIO_SINGLE)
40 #else
41 #define IMAGEIO_NUM (IMAGEIO_DOUBLE)
42 #endif
43 
45 typedef struct
46 {
48  num *Data;
50  int Width;
52  int Height;
55 } image;
56 
57 
59 typedef struct
60 {
62  char *InputFile;
64  char *OutputFile;
66  int JpegQuality;
67 
69  char *Model;
71  num Sigma;
73  num Lambda;
74 } programparams;
75 
76 
79 {
80  puts(
81  "Total variation regularized denoising IPOL demo, P. Getreuer, 2012\n\n"
82  "Syntax: tvdenoise [options] <noisy> <denoised>\n");
83  puts("where <noisy> and <denoised> are "
84  READIMAGE_FORMATS_SUPPORTED " images.\n");
85  puts(
86  "Either lambda (the fidelty strength) or sigma (the noise standard\n"
87  "deviation) should be specified. If sigma is specified, then lambda is\n"
88  "selected automatically using Chambolle's algorithm.\n");
89  puts("Options:");
90  puts(" -n <model> Specify noise model, where <model> is\n"
91  " gaussian Additive white Gaussian noise\n"
92  " Y[n] ~ Normal(X[n], sigma^2)");
93  puts(" laplace Laplace noise\n"
94  " Y[n] ~ Laplace(X[n], sigma/sqrt(2))\n"
95  " poisson Poisson noise\n"
96  " Y[n] ~ Poisson(X[n]/a) a\n"
97  " where a = 255 sigma^2 / (mean X)");
98  puts(" -n <model>:<sigma> Specify sigma, the noise standard deviation");
99  puts(" -l <number> Specify lambda, the fidelity stength\n");
100 #ifdef USE_LIBJPEG
101  puts(" -q <number> Quality for saving JPEG images (0 to 100)\n");
102 #endif
103  puts("Example:\n"
104  " tvdenoise -n laplace:10 noisy.bmp denoised.bmp\n");
105 }
106 
107 int Denoise(image u, image f, const char *Model, num Sigma, num Lambda);
108 num ComputeRmse(image f, image u);
109 int IsGrayscale(image f);
110 static int ParseParams(programparams *Param, int argc, char *argv[]);
111 
112 
113 int main(int argc, char **argv)
114 {
115  programparams Param;
116  image f = {NULL, 0, 0, 0}, u = {NULL, 0, 0, 0};
117  int Status = 1;
118 
119  /* Read command line arguments */
120  if(!ParseParams(&Param, argc, argv))
121  return 0;
122 
123  /* Read the input image */
124  if(!(f.Data = (num *)ReadImage(&f.Width, &f.Height, Param.InputFile,
125  IMAGEIO_RGB | IMAGEIO_PLANAR | IMAGEIO_NUM)))
126  goto Catch;
127 
128  f.NumChannels = IsGrayscale(f) ? 1 : 3;
129  u = f;
130 
131  /* Allocate space for the noisy and denoised image */
132  if(!(u.Data = (num *)Malloc(sizeof(num) * ((size_t)f.Width)
133  * ((size_t)f.Height) * f.NumChannels)))
134  goto Catch;
135 
136  /* Denoise the image */
137  if(!Denoise(u, f, Param.Model, Param.Sigma, Param.Lambda))
138  goto Catch;
139 
140  /* Write the denoised image */
141  if(!WriteImage(u.Data, u.Width, u.Height, Param.OutputFile,
142  ((u.NumChannels == 1) ? IMAGEIO_GRAYSCALE : IMAGEIO_RGB)
143  | IMAGEIO_PLANAR | IMAGEIO_NUM, JPEGQUALITY))
144  goto Catch;
145 
146  Status = 0;
147 Catch:
148  if(u.Data)
149  Free(u.Data);
150  if(f.Data)
151  Free(f.Data);
152  return Status;
153 }
154 
155 
171 int LambdaTune(tvregopt *Opt, image u, image f, const char *Model, num Sigma)
172 {
173  num Lambda, Rmse;
174  int k;
175 
176  /* Empirical estimates of the optimal lambda value
177  (here, Sigma is scaled relative to intensities in [0,1]) */
178  if(!strcmp(Model, "gaussian"))
179  Lambda = 0.7079 / Sigma + 0.002686 / (Sigma * Sigma);
180  else if(!strcmp(Model, "laplace"))
181  Lambda = (-0.00416*Sigma + 0.001301)
182  / (((Sigma - 0.2042)*Sigma + 0.01635)*Sigma + 5.836e-4);
183  else if(!strcmp(Model, "poisson"))
184  Lambda = 0.2839 / Sigma + 0.001502 / (Sigma * Sigma);
185  else
186  {
187  fprintf(stderr, "Unrecognized noise model \"%s\"\n", Model);
188  return 0;
189  }
190 
191  if(Lambda < 1e-4) /* Prevent nonpositive Lambda */
192  Lambda = 1e-4;
193 
194  TvRegSetLambda(Opt, Lambda);
195 
196  printf("Tuning lambda...\n\n");
197  printf(" lambda distance (target = %.5f)\n", DISPLAY_SCALING * Sigma);
198  printf(" --------------------\n");
199  printf(" %-9.4f", Lambda);
200 
201  for(k = 0; k < LAMBDA_TUNE_ITERATIONS; k++)
202  {
203  /* Each TvRestore uses the current u as the initial guess and
204  overwrites it with the denoising result. This speeds up the
205  computation because the result using the previous Lambda value
206  is a good estimate for the next Lambda value. */
207  if(!TvRestore(u.Data, f.Data, f.Width, f.Height, f.NumChannels, Opt))
208  {
209  fprintf(stderr, "Error in computation.\n");
210  return 0;
211  }
212 
213  Rmse = ComputeRmse(f, u);
214 
215  if(!strcmp(Model, "laplace"))
216  Lambda *= sqrt(Rmse/Sigma);
217  else
218  Lambda *= Rmse/Sigma;
219 
220  TvRegSetLambda(Opt, Lambda);
221  printf(" %.5f\n %-9.4f", DISPLAY_SCALING * Rmse, Lambda);
222  }
223 
224  return 1;
225 }
226 
227 
237 int Denoise(image u, image f, const char *Model, num Sigma, num Lambda)
238 {
239  tvregopt *Opt = NULL;
240  int Success = 0;
241 
242  if(!(Opt = TvRegNewOpt()))
243  {
244  fprintf(stderr, "Memory allocation failed\n");
245  return 0;
246  }
247  else if(!(TvRegSetNoiseModel(Opt, Model)))
248  {
249  fprintf(stderr, "Unrecognized noise model \"%s\"\n", Model);
250  return 0;
251  }
252 
253  printf("TV regularized denoising with %c%s noise model\n",
254  toupper(*Model), Model + 1);
255 
256  /* Set initial guess as u = f */
257  memcpy(u.Data, f.Data, sizeof(num)*f.Width*f.Height*f.NumChannels);
258  TvRegSetPlotFun(Opt, NULL, NULL);
259  TvRegSetTol(Opt, (num)1e-2);
260  TvRegSetMaxIter(Opt, 40);
261 
262  if(Sigma <= 0)
263  TvRegSetLambda(Opt, Lambda);
264  else if(!LambdaTune(Opt, u, f, Model, Sigma))
265  goto Catch;
266 
267  /* Final denoising */
268  TvRegSetTol(Opt, (num)5e-4);
269  TvRegSetMaxIter(Opt, 100);
270 
271  if(!TvRestore(u.Data, f.Data, f.Width, f.Height, f.NumChannels, Opt))
272  {
273  fprintf(stderr, "Error in computation.\n");
274  goto Catch;
275  }
276 
277  if(Sigma > 0)
278  printf(" %.5f\n\n", DISPLAY_SCALING * ComputeRmse(f, u));
279 
280  Success = 1;
281 Catch:
282  TvRegFreeOpt(Opt);
283  return Success;
284 }
285 
286 
289 {
290  num Temp, Rmse = 0;
291  long n, N = ((long)u.Width) * ((long)u.Height) * u.NumChannels;
292 
293  for(n = 0; n < N; n++)
294  {
295  Temp = f.Data[n] - u.Data[n];
296  Rmse += Temp*Temp;
297  }
298 
299  return sqrt(Rmse/N);
300 }
301 
302 
305 {
306  const long NumPixels = ((long)f.Width) * ((long)f.Height);
307  const num *Red = f.Data;
308  const num *Green = f.Data + NumPixels;
309  const num *Blue = f.Data + 2*NumPixels;
310  long n;
311 
312  for(n = 0; n < NumPixels; n++)
313  if(Red[n] != Green[n] || Red[n] != Blue[n])
314  return 0;
315 
316  return 1;
317 }
318 
319 
320 static int ParseParams(programparams *Param, int argc, char *argv[])
321 {
322  char *OptionString, *Sigma;
323  char OptionChar;
324  int i;
325 
326  if(argc < 2)
327  {
329  return 0;
330  }
331 
332  /* Set parameter defaults */
333  Param->InputFile = NULL;
334  Param->OutputFile = NULL;
335  Param->Model = "gaussian";
336  Param->Sigma = -1;
337  Param->Lambda = -1;
338  Param->JpegQuality = 95;
339 
340  for(i = 1; i < argc;)
341  {
342  if(argv[i] && argv[i][0] == '-')
343  {
344  if((OptionChar = argv[i][1]) == 0)
345  {
346  ErrorMessage("Invalid parameter format.\n");
347  return 0;
348  }
349 
350  if(argv[i][2])
351  OptionString = &argv[i][2];
352  else if(++i < argc)
353  OptionString = argv[i];
354  else
355  {
356  ErrorMessage("Invalid parameter format.\n");
357  return 0;
358  }
359 
360  switch(OptionChar)
361  {
362  case 'n':
363  Sigma = strchr(OptionString, ':');
364  Param->Model = OptionString;
365 
366  if((Sigma = strchr(OptionString, ':')))
367  {
368  *Sigma = '\0';
369  Param->Sigma = (num)(atof(Sigma + 1)
370  / DISPLAY_SCALING);
371 
372  if(Param->Sigma <= 0)
373  {
374  ErrorMessage("sigma must be positive.\n");
375  return 0;
376  }
377  }
378  break;
379  case 'l':
380  Param->Lambda = (num)atof(OptionString);
381 
382  if(Param->Lambda <= 0)
383  {
384  ErrorMessage("lambda must be positive.\n");
385  return 0;
386  }
387  break;
388 #ifdef LIBJPEG_SUPPORT
389  case 'q':
390  Param->JpegQuality = atoi(OptionString);
391 
392  if(Param->JpegQuality <= 0 || Param->JpegQuality > 100)
393  {
394  ErrorMessage("JPEG quality must be between 0 and 100.\n");
395  return 0;
396  }
397  break;
398 #endif
399  case '-':
401  return 0;
402  default:
403  if(isprint(OptionChar))
404  ErrorMessage("Unknown option \"-%c\".\n", OptionChar);
405  else
406  ErrorMessage("Unknown option.\n");
407 
408  return 0;
409  }
410 
411  i++;
412  }
413  else
414  {
415  if(!Param->InputFile)
416  Param->InputFile = argv[i];
417  else
418  Param->OutputFile = argv[i];
419 
420  i++;
421  }
422  }
423 
424  if(!Param->InputFile)
425  {
427  return 0;
428  }
429 
430  if(Param->Sigma < 0 && Param->Lambda < 0)
431  {
432  ErrorMessage("Either sigma or lambda must be specified.\n");
433  return 0;
434  }
435 
436  return 1;
437 }
438