Total Variation Inpainting using Split Bregman
randmask.c
Go to the documentation of this file.
1 
16 #include <stdio.h>
17 #include <string.h>
18 #include "randmt.h"
19 #include "drawtext.h"
20 #include "imageio.h"
21 
23 #define JPEGQUALITY 95
24 
25 #define CLAMP(X,A,B) (((X) <= (A)) ? (A) : (((X) >= (B)) ? (B) : (X)))
26 
27 
28 void PrintHelpMessage()
29 {
30  puts("Generate a random inpainting mask, P. Getreuer, Y. Wang, 2012\n\n"
31  "Syntax: randmask <type> <input> <output>\n");
32  puts("where <input> and <output> are "
33  READIMAGE_FORMATS_SUPPORTED " images.\n");
34  puts("Choices for <type>\n");
35  puts(
36  " dots:<r> Random dots of radius <r>\n"
37  " scribble:<r> Random scribble with pen of radius <r>\n"
38  " Bernoulli:<p> Random pixel mask with masking ratio <p>\n"
39  " text Random text\n");
40  puts("Example:\n"
41  " randmask scribble:3 input.bmp mask.bmp\n");
42 }
43 
44 
45 int Dots(unsigned char *Mask, int Width, int Height, int PenRadius);
46 int Scribble(unsigned char *Mask, int Width, int Height, int PenRadius);
47 void RandomText(unsigned char *Mask, int Width, int Height);
48 int Bernoulli(unsigned char *Mask, int Width, int Height, double Ratio);
49 
50 int main(int argc, char **argv)
51 {
52  const char *Type, *InputFile, *OutputFile;
53  char *ParamString;
54  unsigned char *Image = NULL;
55  double Param = 1;
56  int Width, Height, Status = 1;
57 
58  if(argc != 4)
59  {
61  return 0;
62  }
63 
64  /* Read command line arguments */
65  if((ParamString = strchr(argv[1], ':')))
66  {
67  Param = atof(ParamString + 1);
68  *ParamString = '\0';
69  }
70 
71  if(Param < 0)
72  Param = 0;
73 
74  Type = argv[1];
75  InputFile = argv[2];
76  OutputFile = argv[3];
77 
78  /* Read the input image */
79  if(!(Image = (unsigned char *)ReadImage(&Width, &Height, InputFile,
80  IMAGEIO_GRAYSCALE | IMAGEIO_U8)))
81  goto Catch;
82 
83  memset(Image, 0, ((long)Width) * ((long)Height));
84 
85  /* Initialize random number generator */
87 
88  if(!strcmp(Type, "dots"))
89  {
90  if(!Dots(Image, Width, Height, (int)Param))
91  goto Catch;
92  }
93  else if(!strcmp(Type, "scribble"))
94  {
95  if(!Scribble(Image, Width, Height, (int)Param))
96  goto Catch;
97  }
98  else if(!strcmp(Type, "Bernoulli"))
99  {
100  if(!Bernoulli(Image, Width, Height, Param))
101  goto Catch;
102  }
103  else if(!strcmp(Type, "text"))
104  RandomText(Image, Width, Height);
105  else
106  {
107  fprintf(stderr, "Unknown mask type, \"%s\".\n", Type);
108  goto Catch;
109  }
110 
111  /* Write the mask image */
112  if(!WriteImage(Image, Width, Height, OutputFile,
113  IMAGEIO_GRAYSCALE | IMAGEIO_U8, JPEGQUALITY))
114  {
115  fprintf(stderr, "Error writing to \"%s\".\n", OutputFile);
116  goto Catch;
117  }
118 
119  Status = 0;
120 Catch:
121  Free(Image);
122  return Status;
123 }
124 
125 
126 unsigned char *MakePen(int PenRadius)
127 {
128  const int PenRadiusSquared = (int)((PenRadius + 0.5f)*(PenRadius + 0.5f));
129  const int PenWidth = 2*PenRadius + 1;
130  unsigned char *Pen = NULL;
131  int x, y, i;
132 
133  if((Pen = calloc(PenWidth*PenWidth, sizeof(unsigned char))))
134  for(y = -PenRadius, i = 0; y <= PenRadius; y++)
135  for(x = -PenRadius; x <= PenRadius; x++, i++)
136  if(x*x + y*y <= PenRadiusSquared)
137  Pen[i] = 1;
138 
139  return Pen;
140 }
141 
142 
143 void PutPixel(unsigned char *Image, int Width, int Height,
144  int x, int y)
145 {
146  if(0 <= x && x < Width && 0 <= y && y < Height)
147  Image[x + Width*y] = 255;
148 }
149 
150 
151 void PutPen(unsigned char *Image, int Width, int Height,
152  unsigned char *Pen, int PenRadius, int x0, int y0)
153 {
154  int x, y, i;
155 
156  for(y = -PenRadius, i = 0; y <= PenRadius; y++)
157  for(x = -PenRadius; x <= PenRadius; x++, i++)
158  if(Pen[i])
159  PutPixel(Image, Width, Height, x0 + x, y0 + y);
160 }
161 
162 
163 unsigned char GetPixel(unsigned char *Image,
164  int Width, int Height, int x, int y)
165 {
166  if(0 <= x && x < Width && 0 <= y && y < Height)
167  return Image[x + Width*y];
168  else
169  return 0;
170 }
171 
172 
180 int Dots(unsigned char *Mask, int Width, int Height, int PenRadius)
181 {
182  const long NumPoints = (long)
183  (2 * sqrt(((float)Width) * ((float)Height))/PenRadius + 0.5);
184  unsigned char *Pen = NULL;
185  long i;
186 
187  if(!(Pen = MakePen(PenRadius)))
188  return 0;
189 
190  for(i = 0; i < NumPoints; i++)
191  PutPen(Mask, Width, Height, Pen, PenRadius,
192  (int)(Width * rand_unif()), (int)(Height * rand_unif()));
193 
194  Free(Pen);
195  return 1;
196 }
197 
204 int Bernoulli(unsigned char *Image, int Width, int Height, double Ratio)
205 {
206  const long NumPixels = ((long)Width) * ((long)Height);
207  long i;
208 
209  if(Ratio < 0 || Ratio > 1)
210  {
211  fprintf(stderr, "Invalid ratio %1.1f "
212  "(should be in the interval [0,1]).\n", Ratio);
213  return 0;
214  }
215 
216  for(i = 0; i < NumPixels; i++)
217  if(rand_unif() < Ratio)
218  Image[i] = 255;
219 
220  return 1;
221 }
222 
234 int Scribble(unsigned char *Mask, int Width, int Height, int PenRadius)
235 {
236  const float Accel = 0.15f;
237  const float Force = 0.1f;
238  const int NumPaths = 20;
239  const long NumPoints = (long)
240  (5 * sqrt(((float)Width) * ((float)Height)) + 0.5);
241  unsigned char *Pen = NULL;
242  float (*Path)[2] = NULL, (*BestPath)[2] = NULL;
243  float MeanX, MeanY, Variance, BestVariance = 0;
244  float PosX, PosY, VelocityX, VelocityY, VelocityMag;
245  long i;
246  int j, Success = 0;
247 
248  if(!(Pen = MakePen(PenRadius))
249  || !(Path = Malloc(sizeof(float)*2*NumPoints))
250  || !(BestPath = Malloc(sizeof(float)*2*NumPoints)))
251  goto Catch;
252 
253  for(j = 0; j < NumPaths; j++)
254  {
255  /* Generate a random path */
256  PosX = (float)(rand_unif() * Width);
257  PosY = (float)(rand_unif() * Height);
258  VelocityX = (float)rand_normal();
259  VelocityY = (float)rand_normal();
260  VelocityMag = (float)sqrt(VelocityX*VelocityX + VelocityY*VelocityY);
261  VelocityX /= VelocityMag;
262  VelocityY /= VelocityMag;
263 
264  for(i = 0; i < NumPoints; i++)
265  {
266  Path[i][0] = PosX;
267  Path[i][1] = PosY;
268 
269  VelocityX +=
270  Accel*((float)rand_normal() + (Width/2 - PosX)*Force/Width);
271  VelocityY +=
272  Accel*((float)rand_normal() + (Height/2 - PosY)*Force/Height);
273 
274  VelocityMag = (float)sqrt(VelocityX*VelocityX
275  + VelocityY*VelocityY);
276 
277  if(VelocityMag > 1)
278  {
279  VelocityX /= VelocityMag;
280  VelocityY /= VelocityMag;
281  }
282 
283  PosX += VelocityX;
284  PosY += VelocityY;
285  PosX = CLAMP(PosX, 0, Width - 1);
286  PosY = CLAMP(PosY, 0, Height - 1);
287 
288  if((PosX <= 0 && VelocityX < 0)
289  || (PosX >= Width - 1 && VelocityX > 0))
290  VelocityX = 0;
291 
292  if((PosY <= 0 && VelocityY < 0)
293  || (PosY >= Height - 1 && VelocityY > 0))
294  VelocityY = 0;
295  }
296 
297  /* Compute the variance of the path */
298  for(i = 0, MeanX = MeanY = 0; i < NumPoints; i++)
299  {
300  MeanX += Path[i][0];
301  MeanY += Path[i][1];
302  }
303 
304  MeanX /= NumPoints;
305  MeanY /= NumPoints;
306 
307  for(i = 0, Variance = 0; i < NumPoints; i++)
308  Variance += (Path[i][0] - MeanX)*(Path[i][0] - MeanX)
309  + (Path[i][1] - MeanY)*(Path[i][1] - MeanY);
310 
311  if(Variance > BestVariance)
312  {
313  BestVariance = Variance;
314  memcpy(BestPath, Path, sizeof(float)*2*NumPoints);
315  }
316  }
317 
318  for(i = 0; i < NumPoints; i++)
319  PutPen(Mask, Width, Height, Pen, PenRadius,
320  (int)BestPath[i][0], (int)BestPath[i][1]);
321 
322  Success = 1;
323 Catch:
324  if(BestPath)
325  Free(BestPath);
326  if(Path)
327  Free(Path);
328  if(Pen)
329  Free(Pen);
330  return Success;
331 }
332 
333 
342 void RandomText(unsigned char *Mask, int Width, int Height)
343 {
344  /* Several text samples about TV inpainting */
345  static const char Text0[] =
346  "In the context of digital images, inpainting is used to restore regions "
347  "of an image that are corrupted by noise or where the data is missing. "
348  "Inpainting is also used to solve disocclusion, to estimate the scene "
349  "behind an obscuring foreground object. Inpainting is an interpolation "
350  "problem, filling the unknown region with a condition to agree with the "
351  "known image on the boundary. ";
352  static const char Text1[] =
353  "However, Laplace's equation is usually unsatisfactory for images since "
354  "it is overly smooth. It cannot recover a step edge passing through the "
355  "region. Total variation (TV) regularization is an effective inpainting "
356  "technique which is capable of recovering sharp edges under some "
357  "conditions (these conditions will be explained). The use of TV "
358  "regularization was originally developed for image denoising by Rudin, "
359  "Osher, and Fatemi [3] and then applied to inpainting by Chan and Shen. ";
360  static const char Text2[] =
361  "The first variational approach to the image inpainting problem was "
362  "Nitzberg and Mumford's 2.1-D sketch [2], based on a variant of the "
363  "Mumford-Shah functional, and the second variational approach was the "
364  "work of Masnou and Morel [6], based on solving for level lines with "
365  "minimal curvature. Bertalmio, Sapiro, Caselles, and Ballester [8] "
366  "introduced the term \"image inpainting\" in analogy to artistic "
367  "inpainting and proposed an anisotropic diffusion PDE model. ";
368  static const char Text3[] =
369  "TV inpainting prefers straight contours as they have minimal TV, but "
370  "this is less successful for recovering curved boundaries. TV "
371  "inpainting can reconstruct a stripe passing through the inpainting "
372  "domain, but only if the length to be bridged is less than the stripe "
373  "thickness. TV inpainting breaks the stripe if the length is greater. "
374  "TV inpainting prefers straight contours as they have minimal TV, but "
375  "this is less successful for recovering curved boundaries. ";
376  const char *Text;
377  int TextLength;
378  char LineBuffer[1024];
379  int i, j, y;
380 
381  for(y = -4; y < Height; y += 19)
382  {
383  i = (int)(4*rand_unif());
384 
385  if(i == 0)
386  Text = Text0;
387  else if(i == 1)
388  Text = Text1;
389  else if(i == 2)
390  Text = Text2;
391  else
392  Text = Text3;
393 
394  TextLength = strlen(Text);
395  i = (int)(rand_unif()*TextLength);
396  j = 0;
397 
398  do
399  {
400  LineBuffer[j] = Text[i % TextLength];
401  LineBuffer[j + 1] = '\0';
402  i++;
403  j++;
404  }while(j < 1023 && TextWidth(LineBuffer) < Width + 5);
405 
406  /* Draw the text with an 18-point sans serif font */
407  DrawText(Mask, Width, Height, -2, y, 255, LineBuffer);
408  }
409 }
410