Image Interpolation with Geometric Contour Stencils
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros
sinterpcli.c
Go to the documentation of this file.
1 
21 #include <ctype.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include "imageio.h"
25 #include "sinterp.h"
26 
27 
28 #ifndef DOXYGEN_SHOULD_SKIP_THIS
29 
30 /* Default program parameters */
31 #define DEFAULT_PSF_SIGMA 0.35
32 #define DEFAULT_RHO_SIGMA_TANGENT 1.2
33 #define DEFAULT_RHO_SIGMA_NORMAL 0.6
34 #define DEFAULT_REFINEMENT_PASSES 2
35 
36 
37 /* Colors for drawing contour lines, adapted from the
38  * Tango Desktop Project
39  * http://tango.freedesktop.org/Tango_Desktop_Project
40  */
41 const float ColorLine[3] = {0.35f, 0.60f, 0.86f};
42 const float ColorCurve1[3] = {0.54f, 0.88f, 0.20f};
43 const float ColorCurve2[3] = {0.98f, 0.91f, 0.22f};
44 const float ColorCorner[3] = {1.00f, 0.17f, 0.16f};
45 
46 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
47 
48 
50 typedef struct
51 {
53  const char *InputFile;
55  const char *OutputFile;
57  const char *BgFile;
59  int JpegQuality;
60 
63 
65  double ScaleFactor;
67  int CenteredGrid;
69  double (*Psf)(double, double, const void *Param);
71  double PsfParam;
73  double PsfRadius;
74 
75  /* Method parameters */
83 
84 
85 static int ParseParams(programparams *Param, int argc, char *argv[]);
86 
87 
89 static void PrintHelpMessage()
90 {
91  printf("Contour stencil interpolation demo, P. Getreuer 2011\n\n");
92  printf("Usage: sinterp [options] <input file> <output file>\n\n"
93  "Only " READIMAGE_FORMATS_SUPPORTED " images are supported.\n\n");
94  printf("Options:\n");
95  printf(" -x <number> the scale factor\n");
96  printf(" -p <number> sigma_h, the blur size of the point spread function\n");
97  printf(" -g <grid> grid to use for resampling, choices for <grid> are\n"
98  " centered grid with centered alignment (default)\n"
99  " topleft the top-left anchored grid\n\n");
100  printf(" -s show the estimated contours instead of interpolating\n\n");
101  printf(" -t <number> sigma_tau, spread of rho in the tagential direction\n");
102  printf(" -n <number> sigma_nu, spread of rho in the normal direction\n");
103  printf(" -r <number> the number of refinement passes\n\n");
104 #ifdef USE_LIBJPEG
105  printf(" -q <number> quality for saving JPEG images (0 to 100)\n\n");
106 #endif
107  printf("When the -s option is given, the output file may be EPS, SVG, or a\n");
108  printf("supported raster format. Only for SVG, the background image must be\n");
109  printf("written as a separate (raster) image, with the syntax\n");
110  printf(" sinterp [options] -s <input file> <output.svg> <bg image file>\n\n");
111  printf("Example: 4x scaling, sigma_h = 0.5, 2 refinement passes\n"
112  " sinterp -x 4 -p 0.5 -r 2 frog.bmp frog-4x.bmp\n");
113 }
114 
115 
116 /* The point spread function (PSF) */
117 static double GaussianPsf(double x, double y, const void *Param)
118 {
119  const double Sigma = *((double *)Param);
120  const double SigmaSqr = Sigma*Sigma;
121  return exp(-(x*x + y*y)/(2.0*SigmaSqr))/(M_2PI*SigmaSqr);
122 }
123 
124 
126 double LinearPatch(ATTRIBUTE_UNUSED double x, double y,
127  ATTRIBUTE_UNUSED const void *Param)
128 {
129  return y;
130 }
131 
133 int DrawLineStencil(pen *Pen, ATTRIBUTE_UNUSED const void *Param)
134 {
135  return PenDrawLine(Pen, -1, 0, 1, 0);
136 }
137 
138 
140 double Corner(double x, double y, ATTRIBUTE_UNUSED const void *Param)
141 {
142  return (x < y) ? x : y;
143 }
144 
146 int DrawCornerStencil(pen *Pen, ATTRIBUTE_UNUSED const void *Param)
147 {
148  return PenDrawLine(Pen, 1, 0, 0, 0) && PenDrawLine(Pen, 0, 0, 0, 1);
149 }
150 
151 
153 double TJunction(double x, double y, ATTRIBUTE_UNUSED const void *Param)
154 {
155  y += 1;
156  return (fabs(x) < y) ? fabs(x) : y;
157 }
158 
159 
161 double CurveDist(double x, double y, const void *Param)
162 {
163  const double a = *((const double *)Param);
164  const double a2 = a*a;
165  const double xa = fabs(x)/a2;
166  const double z = (1 - a*y)*(2/(3*a2));
167  double x0, Temp;
168 
169  if((Temp = xa*xa + z*z*z) >= 0)
170  {
171  Temp = sqrt(Temp);
172  x0 = pow(xa + Temp, 1.0/3.0)
173  + ((a*y < 1) ? -1:1)*pow(fabs(-xa + Temp), 1.0/3.0);
174  }
175  else
176  {
177  Temp = sqrt(-z);
178  x0 = 2*Temp*cos(acos(xa/(-z*Temp))/3);
179  }
180 
181  Temp = x0*x0;
182  return sqrt(a2*Temp + 1)*((a/2)*Temp - y);
183 }
184 
186 int DrawCurveStencil(pen *Pen, const void *Param)
187 {
188  const double a = *((const double *)Param);
189  const double x = 0.985;
190 
191  return PenDrawQBezier(Pen, -x, 0.5*a*x*x, 0, -0.5*a*x*x, x, 0.5*a*x*x);
192 }
193 
194 
202 {
203  const int FilterScaleFactor = 2;
204  sinterp *SInterp;
205  float *Input = NULL, *Filtered = NULL, *Output = NULL;
206  float *FilterRhoSamples = NULL, *RhoSamples = NULL;
207  int *BestStencil = NULL;
208  unsigned long StartTime0, StartTime;
209  int IntegerScaleFactor = (int)(Param.ScaleFactor + 0.5);
210  int InputWidth, InputHeight, OutputWidth, OutputHeight, OutputNumEl;
211  int i, Success = 1;
212 
213 
214  printf("Interp stencils... \t");
215  StartTime = Clock();
216 
217  if(!(SInterp = NewSInterp(StencilSet,
218  (float)Param.RhoSigmaTangent, (float)Param.RhoSigmaNormal)))
219  goto Catch;
220 
221  printf("%7.3f s\n", (Clock() - StartTime)*0.001f);
222 
223  if(Param.RefinementPasses || IntegerScaleFactor == Param.ScaleFactor)
224  {
225  printf("Precomputing rho... \t");
226  StartTime = Clock();
227 
228  if(Param.RefinementPasses)
229  if(!(FilterRhoSamples = ComputeRhoSamples(SInterp,
230  FilterScaleFactor, Param.CenteredGrid)))
231  goto Catch;
232 
233  if(IntegerScaleFactor == Param.ScaleFactor)
234  if(!(RhoSamples = ComputeRhoSamples(SInterp,
235  (int)ceil(Param.ScaleFactor), Param.CenteredGrid)))
236  goto Catch;
237 
238  printf("%7.3f s\n", (Clock() - StartTime)*0.001f);
239  }
240 
241  if(!(Input = (float *)ReadImage(&InputWidth, &InputHeight,
242  Param.InputFile, IMAGEIO_FLOAT | IMAGEIO_RGB)))
243  goto Catch;
244 
245  OutputWidth = (int)ceil(Param.ScaleFactor*InputWidth);
246  OutputHeight = (int)ceil(Param.ScaleFactor*InputHeight);
247  OutputNumEl = 3*OutputWidth*OutputHeight;
248  printf("\n%gx interpolation, %dx%d -> %dx%d\n", Param.ScaleFactor,
249  InputWidth, InputHeight, OutputWidth, OutputHeight);
250 
251  if(Param.CenteredGrid)
252  printf("centered grid, ");
253  else
254  printf("top-left grid, ");
255 
256  if(Param.RefinementPasses == 1)
257  printf("1 refinement pass");
258  else if(Param.RefinementPasses > 1)
259  printf("%d refinement passes", Param.RefinementPasses);
260 
261  printf("\nsigma_h = %g, sigma_tau = %g, sigma_nu = %g\n",
262  Param.PsfParam, Param.RhoSigmaTangent,
263  Param.RhoSigmaNormal);
264 
265  if(!(Output = (float *)Malloc(sizeof(float)*3*OutputWidth*OutputHeight))
266  || !(Filtered = (float *)Malloc(sizeof(float)*3*InputWidth*InputHeight))
267  || !(BestStencil = (int *)Malloc(sizeof(int)*InputWidth*InputHeight)))
268  goto Catch;
269 
270  StartTime0 = Clock();
271 
272  printf("Fitting stencils... \t");
273  StartTime = Clock();
274  FitStencils(BestStencil, StencilSet, Input, InputWidth, InputHeight);
275  printf("%7.3f s\n", (Clock() - StartTime)*0.001f);
276 
277  if(Param.RefinementPasses)
278  {
279  printf("Prefiltering... \t");
280  StartTime = Clock();
281 
282  if(!(Prefilter(Filtered, BestStencil, FilterRhoSamples,
283  Input, InputWidth, InputHeight, FilterScaleFactor,
284  Param.CenteredGrid, (float)Param.PsfParam,
285  Param.RefinementPasses)))
286  goto Catch;
287 
288  printf("%7.3f s\n", (Clock() - StartTime)*0.001f);
289  }
290 
291  if(IntegerScaleFactor == Param.ScaleFactor)
292  {
293  printf("Interpolating int... \t");
294  StartTime = Clock();
295 
296  for(i = 0; i < OutputNumEl; i++)
297  Output[i] = 0;
298 
299  IntegerScalePass(Output, BestStencil, RhoSamples,
300  (Param.RefinementPasses) ? Filtered : Input, InputWidth,
301  InputHeight, IntegerScaleFactor, Param.CenteredGrid);
302  }
303  else
304  {
305  printf("Interpolating arb... \t");
306  StartTime = Clock();
307 
308  ArbitraryScale(Output, OutputWidth, OutputHeight, BestStencil,
309  SInterp, (Param.RefinementPasses) ? Filtered : Input,
310  InputWidth, InputHeight, (float)Param.ScaleFactor,
311  Param.CenteredGrid);
312  }
313 
314  printf("%7.3f s\n", (Clock() - StartTime)*0.001f);
315 
316  printf("Total %7.3f s\n", (Clock() - StartTime0)*0.001f);
317 
318  /* Write the output image */
319  if(!WriteImage(Output, OutputWidth, OutputHeight, Param.OutputFile,
320  IMAGEIO_FLOAT | IMAGEIO_RGB, Param.JpegQuality))
321  goto Catch;
322 
323  Success = 1;
324 Catch:
325  Free(Filtered);
326  Free(Output);
327  Free(BestStencil);
328  Free(Input);
329  Free(RhoSamples);
330  Free(FilterRhoSamples);
331  return Success;
332 }
333 
334 
336 static int StringEndsWith(const char *String, const char *Suffix)
337 {
338  unsigned i, StringLength = strlen(String), SuffixLength = strlen(Suffix);
339 
340  if(StringLength < SuffixLength)
341  return 0;
342 
343  String += StringLength - SuffixLength;
344 
345  for(i = 0; i < SuffixLength; i++)
346  if(tolower(String[i]) != tolower(Suffix[i]))
347  return 0;
348 
349  return 1;
350 }
351 
352 
360 {
361  float *Input = NULL, *Output = NULL;
362  int *BestStencil = NULL;
363  unsigned long StartTime;
364  int InputWidth, InputHeight, OutputWidth, OutputHeight;
365  int Success = 0;
366 
367 
368  if(!(Input = (float *)ReadImage(&InputWidth, &InputHeight,
369  Param.InputFile, IMAGEIO_FLOAT | IMAGEIO_RGB)))
370  goto Catch;
371 
372  OutputWidth = (int)ceil(Param.ScaleFactor*InputWidth);
373  OutputHeight = (int)ceil(Param.ScaleFactor*InputHeight);
374 
375  if(!(BestStencil = (int *)Malloc(sizeof(int)*InputWidth*InputHeight)))
376  goto Catch;
377 
378  printf("Fitting stencils... \t");
379  StartTime = Clock();
380  FitStencils(BestStencil, StencilSet, Input, InputWidth, InputHeight);
381  printf("%7.3f s\n", (Clock() - StartTime)*0.001f);
382 
383  if(!(Output = (float *)Malloc(sizeof(float)*3*OutputWidth*OutputHeight)))
384  goto Catch;
385 
386  printf("Drawing contours... \t");
387  StartTime = Clock();
388 
389  if(StringEndsWith(Param.OutputFile, ".eps"))
390  { /* Draw the image as an EPS vector graphic. The background image data
391  is embedded in the EPS. */
392  if(!DrawContoursEps(Param.OutputFile,
393  BestStencil, StencilSet, Input, InputWidth, InputHeight))
394  goto Catch;
395  }
396  else if(StringEndsWith(Param.OutputFile, ".svg"))
397  { /* Draw the image as an SVG vector graphic. The background image is
398  drawn as a raster graphic to Param.BgFile. */
399  if(!DrawContoursSvg(Param.OutputFile, Param.BgFile,
400  BestStencil, StencilSet,
401  Input, InputWidth, InputHeight))
402  goto Catch;
403  }
404  else
405  { /* Draw the image as a raster graphic. */
406  if(!DrawContours(Param.OutputFile, OutputWidth, OutputHeight,
407  Param.ScaleFactor, BestStencil, StencilSet,
408  Input, InputWidth, InputHeight))
409  goto Catch;
410  }
411 
412  printf("%7.3f s\n", (Clock() - StartTime)*0.001f);
413  Success = 1;
414 Catch:
415  Free(Output);
416  Free(BestStencil);
417  Free(Input);
418  return Success;
419 }
420 
421 
422 int main(int argc, char *argv[])
423 {
424  programparams Param;
425  static const double a1 = M_1_SQRT2;
426  static const double a2 = 1;
427  sset *StencilSet;
428  unsigned long StartTime;
429  int S, Status = 1;
430 
431 
432  /* Parse command line parameters */
433  if(!ParseParams(&Param, argc, argv))
434  return 1;
435 
436  printf("Building stencils... \t");
437  StartTime = Clock();
438 
439  if(!(StencilSet = NewStencilSet(2, Param.Psf,
440  (void *)&Param.PsfParam, Param.PsfRadius)))
441  goto Catch;
442 
443  for(S = 0; S < 32; S++)
444  AddStencil(StencilSet, LinearPatch, NULL, S*M_PI_8/4,
445  DrawLineStencil, NULL, ColorLine);
446 
447  for(S = 0; S < 8; S++)
448  AddStencil(StencilSet, CurveDist, &a1, S*M_PI_4,
449  DrawCurveStencil, (const void *)&a1, ColorCurve1);
450 
451  for(S = 0; S < 8; S++)
452  AddStencil(StencilSet, CurveDist, &a2, S*M_PI_4,
453  DrawCurveStencil, (const void *)&a2, ColorCurve2);
454 
455  for(S = 0; S < 8; S++)
456  AddStencil(StencilSet, Corner, NULL, S*M_PI_4,
457  DrawCornerStencil, NULL, ColorCorner);
458 
459  printf("%7.3f s\n", (Clock() - StartTime)*0.001f);
460 
461  /* PrintStencilConfusion(StencilSet); */
462 
463  if(Param.ShowContours)
464  {
465  if(!ShowContours(Param, StencilSet))
466  goto Catch;
467  }
468  else
469  {
470  if(!Interpolate(Param, StencilSet))
471  goto Catch;
472  }
473 
474  Status = 0;
475 Catch:
476  FreeStencilSet(StencilSet);
477  return Status;
478 }
479 
480 
487 static int ParseParams(programparams *Param, int argc, char *argv[])
488 {
489  static const char *DefaultOutputFile = (const char *)"out.bmp";
490  char *OptionString;
491  char OptionChar;
492  int i;
493 
494 
495  if(argc < 2)
496  {
498  return 0;
499  }
500 
501  /* Set parameter defaults */
502  Param->InputFile = NULL;
503  Param->OutputFile = NULL;
504  Param->BgFile = NULL;
505  Param->JpegQuality = 80;
506 
507  Param->ShowContours = 0;
508 
509  Param->ScaleFactor = 4;
510  Param->CenteredGrid = 1;
511  Param->Psf = GaussianPsf;
512  Param->PsfParam = DEFAULT_PSF_SIGMA;
513 
514  Param->RhoSigmaTangent = DEFAULT_RHO_SIGMA_TANGENT;
515  Param->RhoSigmaNormal = DEFAULT_RHO_SIGMA_NORMAL;
516  Param->RefinementPasses = DEFAULT_REFINEMENT_PASSES;
517 
518  for(i = 1; i < argc;)
519  {
520  if(argv[i] && argv[i][0] == '-')
521  {
522  if((OptionChar = argv[i][1]) == 0)
523  {
524  ErrorMessage("Invalid parameter format.\n");
525  return 0;
526  }
527 
528  if(argv[i][2])
529  OptionString = &argv[i][2];
530  else if(++i < argc)
531  OptionString = argv[i];
532  else
533  {
534  ErrorMessage("Invalid parameter format.\n");
535  return 0;
536  }
537 
538  switch(OptionChar)
539  {
540  case 'x':
541  Param->ScaleFactor = atof(OptionString);
542 
543  if(Param->ScaleFactor < 1)
544  {
545  ErrorMessage("Scale factor cannot be less than 1.0.\n");
546  return 0;
547  }
548  break;
549  case 'g':
550  if(!strcmp(OptionString, "centered")
551  || !strcmp(OptionString, "center"))
552  Param->CenteredGrid = 1;
553  else if(!strcmp(OptionString, "topleft")
554  || !strcmp(OptionString, "top-left"))
555  Param->CenteredGrid = 0;
556  else
557  {
558  ErrorMessage("Grid must be either \"centered\" or \"topleft\".\n");
559  return 0;
560  }
561  break;
562  case 'r':
563  Param->RefinementPasses = atoi(OptionString);
564 
565  if(Param->RefinementPasses < 0 || Param->RefinementPasses > 10000)
566  {
567  ErrorMessage("Invalid number of refinement steps.\n");
568  return 0;
569  }
570  break;
571  case 'p':
572  Param->PsfParam = atof(OptionString);
573 
574  if(Param->PsfParam < 0.0)
575  {
576  ErrorMessage("Point spread blur size must be nonnegative.\n");
577  return 0;
578  }
579  else if(Param->PsfParam > 2.0)
580  {
581  ErrorMessage("Point spread blur size is too large.\n");
582  return 0;
583  }
584  break;
585  case 't':
586  Param->RhoSigmaTangent = atof(OptionString);
587 
588  if(Param->RhoSigmaTangent <= 0.0)
589  {
590  ErrorMessage("sigma_tau must be positive.\n");
591  return 0;
592  }
593  break;
594  case 'n':
595  Param->RhoSigmaNormal = atof(OptionString);
596 
597  if(Param->RhoSigmaNormal <= 0.0)
598  {
599  ErrorMessage("sigma_nu must be positive.\n");
600  return 0;
601  }
602  break;
603  case 's':
604  Param->ShowContours = 1;
605  i--;
606  break;
607 
608 #ifdef USE_LIBJPEG
609  case 'q':
610  Param->JpegQuality = atoi(OptionString);
611 
612  if(Param->JpegQuality <= 0 || Param->JpegQuality > 100)
613  {
614  ErrorMessage("JPEG quality must be between 0 and 100.\n");
615  return 0;
616  }
617  break;
618 #endif
619  case '-':
621  return 0;
622  default:
623  if(isprint(OptionChar))
624  ErrorMessage("Unknown option \"-%c\".\n", OptionChar);
625  else
626  ErrorMessage("Unknown option.\n");
627 
628  return 0;
629  }
630 
631  i++;
632  }
633  else
634  {
635  if(!Param->InputFile)
636  Param->InputFile = argv[i];
637  else if(!Param->OutputFile)
638  Param->OutputFile = argv[i];
639  else
640  Param->BgFile = argv[i];
641 
642  i++;
643  }
644  }
645 
646  if(!Param->InputFile)
647  {
649  return 0;
650  }
651 
652  if(!Param->OutputFile)
653  Param->OutputFile = DefaultOutputFile;
654 
655  if(Param->PsfParam == 0)
656  {
657  Param->Psf = NULL;
658  Param->RefinementPasses = 0;
659  }
660 
661  Param->PsfRadius = 4*Param->PsfParam;
662  return 1;
663 }