Total Variation Inpainting using Split Bregman
tvinpaint.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 JPEGQUALITY 95
34 
35 #ifdef NUM_SINGLE
36 #define IMAGEIO_NUM (IMAGEIO_SINGLE)
37 #else
38 #define IMAGEIO_NUM (IMAGEIO_DOUBLE)
39 #endif
40 
42 typedef struct
43 {
45  num *Data;
47  int Width;
49  int Height;
52 } image;
53 
54 
57 {
58  puts(
59  "Total variation regularized inpainting IPOL demo, P. Getreuer, 2012\n\n"
60  "Syntax: tvinpaint <D> <lambda> <input> <inpainted>\n");
61  puts("where <D>, <input>, and <inpainted> are "
62  READIMAGE_FORMATS_SUPPORTED " images.\n");
63  puts("Example:\n"
64  " tvinpaint mountains-D.bmp 1e3 mountains-f.bmp inpainted.bmp\n");
65 }
66 
67 int Inpaint(image u, image f, image D, num Lambda);
68 num ComputeRmse(image f, image u);
69 void ThresholdD(image D, num Lambda);
70 int IsGrayscale(image f);
71 
72 
73 int main(int argc, char **argv)
74 {
75  const char *InputFile, *DomainFile, *OutputFile;
76  image f = {NULL, 0, 0, 0}, u = {NULL, 0, 0, 0}, D = {NULL, 0, 0, 0};
77  num Lambda;
78  int Status = 1;
79 
80  if(argc != 5)
81  {
83  return 0;
84  }
85 
86  /* Read command line arguments */
87  DomainFile = argv[1];
88  Lambda = (num)atof(argv[2]);
89  InputFile = argv[3];
90  OutputFile = argv[4];
91 
92  /* Read the input images */
93  if(!(f.Data = (num *)ReadImage(&f.Width, &f.Height, InputFile,
94  IMAGEIO_RGB | IMAGEIO_PLANAR | IMAGEIO_NUM)) ||
95  !(D.Data = (num *)ReadImage(&D.Width, &D.Height, DomainFile,
96  IMAGEIO_RGB | IMAGEIO_PLANAR | IMAGEIO_NUM)))
97  goto Catch;
98  else if(f.Width != D.Width || f.Height != D.Height)
99  {
100  fprintf(stderr, "Size mismatch: D is %dx%d but f is %dx%d\n",
101  D.Width, D.Height, f.Width, f.Height);
102  goto Catch;
103  }
104 
105  f.NumChannels = IsGrayscale(f) ? 1 : 3;
106  u = f;
107 
108  /* Allocate space for the inpainted image */
109  if(!(u.Data = (num *)Malloc(sizeof(num) * ((size_t)f.Width)
110  * ((size_t)f.Height) * f.NumChannels)))
111  {
112  fprintf(stderr, "Memory allocation failed.\n");
113  goto Catch;
114  }
115 
116  if(!Inpaint(u, f, D, Lambda))
117  {
118  fprintf(stderr, "Failure!\n");
119  goto Catch;
120  }
121 
122  /* Write inpainted image */
123  if(!WriteImage(u.Data, u.Width, u.Height, OutputFile,
124  ((u.NumChannels == 1) ? IMAGEIO_GRAYSCALE : IMAGEIO_RGB)
125  | IMAGEIO_PLANAR | IMAGEIO_NUM, JPEGQUALITY))
126  fprintf(stderr, "Error writing to \"%s\".\n", OutputFile);
127 
128  Status = 0;
129 Catch:
130  if(u.Data)
131  Free(u.Data);
132  if(D.Data)
133  Free(D.Data);
134  if(f.Data)
135  Free(f.Data);
136  return Status;
137 }
138 
139 
151 int Inpaint(image u, image f, image D, num Lambda)
152 {
153  tvregopt *Opt = NULL;
154  const long NumPixels = ((long)f.Width) * ((long)f.Height);
155  num *Red = D.Data;
156  num *Green = D.Data + NumPixels;
157  num *Blue = D.Data + 2*NumPixels;
158  long n, k;
159  int Success = 0;
160 
161  if(!(Opt = TvRegNewOpt()))
162  {
163  fprintf(stderr, "Memory allocation failed\n");
164  return 0;
165  }
166 
167  memcpy(u.Data, f.Data, sizeof(num)*f.Width*f.Height*f.NumChannels);
168 
169  /* Convert the mask into spatially-varing lambda */
170  for(n = 0; n < NumPixels; n++)
171  if(0.299*Red[n] + 0.587*Green[n] + 0.114*Blue[n] > 0.5)
172  {
173  D.Data[n] = 0; /* Inside of the inpainting domain */
174 
175  /* Set u = 0.5 within D */
176  for(k = 0; k < u.NumChannels; k++)
177  u.Data[n + k*NumPixels] = 0.5;
178  }
179  else
180  D.Data[n] = Lambda; /* Outside of the inpainting domain */
181 
182  TvRegSetVaryingLambda(Opt, D.Data, D.Width, D.Height);
183  TvRegSetMaxIter(Opt, 250);
184  TvRegSetTol(Opt, (num)1e-5);
185 
186  /* TvRestore performs the split Bregman inpainting */
187  if(!TvRestore(u.Data, f.Data, f.Width, f.Height, f.NumChannels, Opt))
188  {
189  fprintf(stderr, "Error in computation.\n");
190  goto Catch;
191  }
192 
193  Success = 1;
194 Catch:
195  TvRegFreeOpt(Opt);
196  return Success;
197 }
198 
199 
202 {
203  const long NumPixels = ((long)f.Width) * ((long)f.Height);
204  const num *Red = f.Data;
205  const num *Green = f.Data + NumPixels;
206  const num *Blue = f.Data + 2*NumPixels;
207  long n;
208 
209  for(n = 0; n < NumPixels; n++)
210  if(Red[n] != Green[n] || Red[n] != Blue[n])
211  return 0;
212 
213  return 1;
214 }