Linear Methods for Image Interpolation
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros
linterpcli.c
Go to the documentation of this file.
1 
21 #include <string.h>
22 #include <ctype.h>
23 #include <math.h>
24 
25 #include "imageio.h"
26 #include "linterp.h"
27 #include "strutil.h"
28 
29 /* Set to 1 for verbose program output */
30 #define VERBOSE 0
31 
32 
34 typedef struct
35 {
37  float *Data;
39  int Width;
41  int Height;
42 } imagef;
43 
44 
46 typedef struct
47 {
49  char *InputFile;
51  char *OutputFile;
53  int JpegQuality;
55  int CenteredGrid;
57  boundaryhandling Boundary;
59  char *ScaleStr;
61  double ScaleX;
63  double ScaleY;
69  float Rotation;
71  char *Method;
73  float PsfSigma;
75 
76 
77 static int ParseParams(programparams *Param, int argc, char *argv[]);
78 static int ParseScaling(programparams *Param, int InputWidth, int InputHeight);
79 
80 static void PrintHelpMessage()
81 {
82  printf("Linear interpolation demo, P. Getreuer 2010-2011\n\n");
83  printf("Usage: linterp [options] <input file> <output file>\n\n"
84  "Only " READIMAGE_FORMATS_SUPPORTED " images are supported.\n\n");
85  printf("Options:\n");
86  printf(" -m <method> interpolation method to apply, choices for <method> are\n");
87  printf(" nearest nearest neighbor (pixel duplication)\n");
88  printf(" bilinear standard bilinear interpolation\n");
89  printf(" bicubic Keys bicubic with parameter -0.5\n");
90  printf(" lanczosN Lanczos radius-N sinc approximation,\n");
91  printf(" N = 2, 3, 4\n");
92  printf(" bsplineN B-spline of degree N,\n");
93  printf(" N = 2, 3, 5, 7, 9, 11\n");
94  printf(" omomsN o-Moms of degree N,\n");
95  printf(" N = 3, 5, 7\n");
96  printf(" fourier Fourier zero-padding (sinc)\n\n");
97  printf(" -x <scale> the scale factor (may be non-integer)\n");
98  printf(" -x <x-scale>,<y-scale> set horizontal and vertical scale factors\n");
99  printf(" -x <width>x<height> set maximum interpolated size in pixels, \n");
100  printf(" preserves aspect ratio\n");
101  printf(" -x <width>x<height>^ set minimum interpolated size in pixels, \n");
102  printf(" preserves aspect ratio\n");
103  printf(" -x <width>x<height>! set actual interpolated size in pixels, \n");
104  printf(" ignores aspect ratio\n\n");
105  printf(" -r <number> rotation, counter clockwise in degrees\n");
106  printf(" (if specified, preserves aspect ratio regardless of -x)\n");
107  printf(" -p <number> sigma_h, the blur size of the point spread function\n");
108  printf(" -b <ext> extension to use for boundary handling, choices for <ext> are\n");
109  printf(" const constant extension\n");
110  printf(" hsym half-sample symmetric\n");
111  printf(" wsym whole-sample symmetric\n");
112  printf(" -g <grid> grid to use for resampling, choices for <grid> are\n"
113  " centered grid with centered alignment (default)\n"
114  " topleft the top-left anchored grid\n\n");
115 #ifdef USE_LIBJPEG
116  printf(" -q <number> quality for saving JPEG images (0 to 100)\n\n");
117 #endif
118  printf("Example: 4.5x cubic B-spline scaling, sigma_h = 0.35\n"
119  " linterp -m bspline3 -x 4.5 -p 0.35 frog.bmp interpolation.bmp\n");
120 }
121 
122 
123 float GaussianPsf(float x, const void *Param)
124 {
125  float Sigma = *((float *)Param);
126  return exp(-(x*x)/(2*Sigma*Sigma)) / (sqrt(M_2PI)*Sigma);
127 }
128 
129 
131 {
132  interpmethod *Method;
133  const int InputNumPixels = v.Width*v.Height;
134  imagef u = {NULL, 0, 0};
135  float *Coeff = NULL, *X = NULL, *Y = NULL;
136  float XStart, XStep, YStart, YStep, Theta;
137  int NumCoeffs, Channel;
138 
139 
140  if(!(Method = GetInterpMethod(Param.Method)))
141  {
142  fprintf(stderr, "Unknown interpolation method.\n");
143  goto Catch;
144  }
145 
146  Theta = Param.Rotation*M_PI/180;
147 
148  /* Prefilter the image if necessary */
149  if(Param.PsfSigma > 0)
150  {
151  if(Param.Boundary != BOUNDARY_HSYMMETRIC
152  && Param.Boundary != BOUNDARY_WSYMMETRIC)
153  {
154  fprintf(stderr,
155 "PSF prefiltering only supported for half- and whole-sample symmetric\n"
156 "boundary extension.\n");
157  goto Catch;
158  }
159 
160  NumCoeffs = 2 + ceil(Method->KernelRadius + 5*Param.PsfSigma);
161 
162  if(!(Coeff = (float *)Malloc(sizeof(float)*NumCoeffs)))
163  {
164  fprintf(stderr, "Memory allocation failed.\n");
165  goto Catch;
166  }
167 
168 #if VERBOSE > 0
169  printf("PSF prefiltering\n");
170 #endif
171  PsfConvCoeff(Coeff, NumCoeffs, GaussianPsf,
172  (const void *)&Param.PsfSigma, Method->Kernel,
173  Method->KernelRadius, Method->KernelNormalize);
174  PsfPreFilter(v.Data, v.Width, v.Height, 3,
175  Coeff, NumCoeffs, Param.Boundary);
176  }
177  else if(Method->PrefilterNumAlpha)
178  {
179 #if VERBOSE > 0
180  printf("Prefiltering\n");
181 #endif
182  PrefilterImage(v.Data, v.Width, v.Height, 3, Method->PrefilterAlpha,
183  Method->PrefilterNumAlpha, Method->PrefilterScale, Param.Boundary);
184  }
185 
186  if(Theta == 0) /* Scaling without rotation */
187  {
188  u.Width = Param.InterpWidth;
189  u.Height = Param.InterpHeight;
190 
191 #if VERBOSE > 0
192  printf("Scaling %dx%d -> %dx%d\n",
193  v.Width, v.Height, u.Width, u.Height);
194 #endif
195 
196  if(!(u.Data = (float *)Malloc(sizeof(float)*3*u.Width*u.Height)))
197  goto Catch;
198 
199  XStep = 1.0/Param.ScaleX;
200  YStep = 1.0/Param.ScaleY;
201 
202  if(Param.CenteredGrid)
203  {
204  XStart = (XStep - 1.0)/2;
205  YStart = (YStep - 1.0)/2;
206  }
207  else
208  XStart = YStart = 0;
209 
210  if(!LinScale2d(u.Data, u.Width, XStart, XStep, u.Height, YStart, YStep,
211  v.Data, v.Width, v.Height, 3, Method->Kernel, Method->KernelRadius,
212  Method->KernelNormalize, Param.Boundary))
213  goto Catch;
214  }
215  else /* Scaling and rotation */
216  {
217  /* Create a list of locations (X[n],Y[n]) that sample a grid of size
218  u.Width by u.Height rotated by Theta and scaled by factor Scale.
219 
220  For other interpolation operations like a perspective transformation,
221  rewrite this step so that (X[n],Y[n]) is the desired nth sampling
222  location and set u.Width and u.Height accordingly. */
223  if(!MakeScaleRotationGrid(&X, &Y, &u.Width, &u.Height,
224  v.Width, v.Height, (Param.ScaleX + Param.ScaleY)/2, Theta))
225  goto Catch;
226 
227 #if VERBOSE > 0
228  printf("Scaling and rotating %dx%d -> %dx%d\n",
229  v.Width, v.Height, u.Width, u.Height);
230 #endif
231 
232  if(!(u.Data = (float *)Malloc(sizeof(float)*3*u.Width*u.Height)))
233  goto Catch;
234 
235  for(Channel = 0; Channel < 3; Channel++)
236  if(!LinInterp2d(u.Data + Channel*u.Width*u.Height,
237  v.Data + Channel*InputNumPixels, v.Width, v.Height,
238  X, Y, u.Width*u.Height, Method->Kernel,
239  Method->KernelRadius, Method->KernelNormalize, Param.Boundary))
240  goto Catch;
241 
242  Free(Y);
243  Free(X);
244  }
245 
246  Free(Coeff);
247  return u;
248 Catch:
249  Free(u.Data);
250  Free(Y);
251  Free(X);
252  Free(Coeff);
253  u.Data = 0;
254  u.Width = u.Height = 0;
255  return u;
256 }
257 
258 
259 int main(int argc, char *argv[])
260 {
261  programparams Param;
262  imagef v = {NULL, 0, 0}, u = {NULL, 0, 0};
263  unsigned long StartTime;
264  float XStart, YStart;
265 
266 
267  if(!ParseParams(&Param, argc, argv))
268  return 0;
269 
270  /* Read the input image */
271  if(!(v.Data = (float *)ReadImage(&v.Width, &v.Height, Param.InputFile,
273  return 1;
274  else if(v.Width < 3 || v.Height < 3)
275  {
276  fprintf(stderr, "Image is too small (%dx%d).\n", v.Width, v.Height);
277  goto Catch;
278  }
279 
280  if(!ParseScaling(&Param, v.Width, v.Height))
281  goto Catch;
282 
283  StartTime = Clock();
284 
285  if(!strcmp(Param.Method, "fourier") || !strcmp(Param.Method, "sinc"))
286  {
287  if(Param.Rotation != 0)
288  {
289  fprintf(stderr, "Rotation is not supported with Fourier interpolation.\n");
290  goto Catch;
291  }
292  else if(Param.Boundary != BOUNDARY_HSYMMETRIC
293  && Param.Boundary != BOUNDARY_WSYMMETRIC)
294  {
295  fprintf(stderr,
296 "Fourier interpolation is only supported for half- and whole-sample\n"
297 "symmetric boundary extension.\n");
298  goto Catch;
299  }
300 
301  u.Width = Param.InterpWidth;
302  u.Height = Param.InterpHeight;
303 
304 #if VERBOSE > 0
305  printf("Fourier scaling %dx%d -> %dx%d\n",
306  v.Width, v.Height, u.Width, u.Height);
307 #endif
308 
309  if(!(u.Data = (float *)Malloc(sizeof(float)*3*u.Width*u.Height)))
310  goto Catch;
311 
312  if(Param.CenteredGrid)
313  {
314  XStart = v.Width/(2.0f*u.Width) - 0.5f;
315  YStart = v.Height/(2.0f*u.Height) - 0.5f;
316  }
317  else
318  XStart = YStart = 0;
319 
320  if(!FourierScale2d(u.Data, u.Width, XStart, u.Height, YStart,
321  v.Data, v.Width, v.Height, 3, Param.PsfSigma, Param.Boundary))
322  goto Catch;
323  }
324  else
325  u = ScaleRotateImage(v, Param);
326 
327  if(!u.Data)
328  {
329  fprintf(stderr, "Error in computation.\n");
330  goto Catch;
331  }
332 
333  printf("CPU Time: %.3f\n", (Clock() - StartTime)*0.001f);
334 
335  /* Write output */
336  if(!WriteImage(u.Data, u.Width, u.Height, Param.OutputFile,
338  fprintf(stderr, "Error writing output file.\n");
339 
340 Catch:
341  Free(u.Data);
342  Free(v.Data);
343  return 0;
344 }
345 
346 
347 static int ParseParams(programparams *Param, int argc, char *argv[])
348 {
349  static char *DefaultOutputFile = (char *)"out.bmp";
350  char *OptionString;
351  char OptionChar;
352  int i;
353 
354 
355  if(argc < 2)
356  {
358  return 0;
359  }
360 
361  /* Set parameter defaults */
362  Param->InputFile = 0;
363  Param->OutputFile = DefaultOutputFile;
364  Param->CenteredGrid = 1;
365  Param->Boundary = BOUNDARY_HSYMMETRIC;
366  Param->ScaleStr = (char *)"1";
367  Param->Rotation = 0;
368  Param->Method = (char *)"bspline3";
369  Param->PsfSigma = 0;
370  Param->JpegQuality = 80;
371 
372  for(i = 1; i < argc;)
373  {
374  if(argv[i] && argv[i][0] == '-')
375  {
376  if((OptionChar = argv[i][1]) == 0)
377  {
378  fprintf(stderr, "Invalid parameter format.\n");
379  return 0;
380  }
381 
382  if(argv[i][2])
383  OptionString = &argv[i][2];
384  else if(++i < argc)
385  OptionString = argv[i];
386  else
387  {
388  fprintf(stderr, "Invalid parameter format.\n");
389  return 0;
390  }
391 
392  switch(OptionChar)
393  {
394  case 'g':
395  if(!strcmp(OptionString, "centered")
396  || !strcmp(OptionString, "center"))
397  Param->CenteredGrid = 1;
398  else if(!strcmp(OptionString, "topleft")
399  || !strcmp(OptionString, "top-left"))
400  Param->CenteredGrid = 0;
401  else
402  {
403  fprintf(stderr, "Grid must be either \"centered\" or \"topleft\".\n");
404  return 0;
405  }
406  break;
407  case 'b':
408  if(!strcmp(OptionString, "const"))
409  Param->Boundary = BOUNDARY_CONSTANT;
410  else if(!strcmp(OptionString, "hsym"))
411  Param->Boundary = BOUNDARY_HSYMMETRIC;
412  else if(!strcmp(OptionString, "wsym"))
413  Param->Boundary = BOUNDARY_WSYMMETRIC;
414  else
415  {
416  fprintf(stderr, "Invalid boundary extension.\n");
417  return 0;
418  }
419  break;
420  case 'x':
421  Param->ScaleStr = OptionString;
422  break;
423  case 'r':
424  Param->Rotation = atof(OptionString);
425  break;
426  case 'm':
427  Param->Method = OptionString;
428  break;
429  case 'p':
430  Param->PsfSigma = atof(OptionString);
431 
432  if(Param->PsfSigma < 0.0)
433  {
434  fprintf(stderr, "Point spread blur size must be nonnegative.\n");
435  return 0;
436  }
437  break;
438 #ifdef USE_LIBJPEG
439  case 'q':
440  Param->JpegQuality = atoi(OptionString);
441 
442  if(Param->JpegQuality <= 0 || Param->JpegQuality > 100)
443  {
444  fprintf(stderr, "JPEG quality must be between 0 and 100.\n");
445  return 0;
446  }
447  break;
448 #endif
449  case '-':
451  return 0;
452  default:
453  if(isprint(OptionChar))
454  fprintf(stderr, "Unknown option \"-%c\".\n", OptionChar);
455  else
456  fprintf(stderr, "Unknown option.\n");
457 
458  return 0;
459  }
460 
461  i++;
462  }
463  else
464  {
465  if(!Param->InputFile)
466  Param->InputFile = argv[i];
467  else
468  Param->OutputFile = argv[i];
469 
470  i++;
471  }
472  }
473 
474  if(!Param->InputFile)
475  {
477  return 0;
478  }
479 
480  /* Display the chosen parameters */
481  printf("Interpolation with %s\n", Param->Method);
482  return 1;
483 }
484 
485 
486 /*
487  * Parse the scaling option string
488  *
489  * Syntax
490  * <scale> Scale by factor <scale>
491  * <scalex>,<scaley> Scale width and height individually
492  * <width>x<height> Max size given, aspect ratio preserved
493  * <width>x<height>^ Min size given, aspect ratio preserved
494  * <width>x<height>! Actual size given, aspect ratio ignored
495  */
496 static int ParseScaling(programparams *Param, int InputWidth, int InputHeight)
497 {
498  const char *StrPtr = Param->ScaleStr;
499  double Number;
500 
501  if(!ParseNumber(&Number, &StrPtr, 1))
502  goto Catch;
503 
504  if(!EatWhitespace(&StrPtr))
505  { /* Syntax <scale> */
506  Param->ScaleX = Param->ScaleY = Number;
507  Param->InterpWidth = (int)ceil(Param->ScaleX*InputWidth);
508  Param->InterpHeight = (int)ceil(Param->ScaleY*InputHeight);
509 #if VERBOSE > 0
510  printf("Scaling by %g (%dx%d to %dx%d)\n", Param->ScaleX,
511  InputWidth, InputHeight,
512  Param->InterpWidth, Param->InterpHeight);
513 #endif
514  }
515  else if(*StrPtr == ',')
516  { /* Syntax <scalex>,<scaley> */
517  StrPtr++;
518  Param->ScaleX = Number;
519 
520  if(!ParseNumber(&Number, &StrPtr, 1))
521  goto Catch;
522 
523  Param->ScaleY = Number;
524  Param->InterpWidth = (int)ceil(Param->ScaleX*InputWidth);
525  Param->InterpHeight = (int)ceil(Param->ScaleY*InputHeight);
526 #if VERBOSE > 0
527  printf("Scaling by %g,%g (%dx%d to %dx%d)\n",
528  Param->ScaleX, Param->ScaleY,
529  InputWidth, InputHeight,
530  Param->InterpWidth, Param->InterpHeight);
531 #endif
532  }
533  else if(*StrPtr == 'x' || *StrPtr == 'X')
534  { /* Syntax <width>x<height>... */
535  StrPtr = Param->ScaleStr;
536 
537  /* Reparse as integer */
538  if(!ParseNumber(&Number, &StrPtr, 0)
539  || !(*StrPtr == 'x' || *StrPtr == 'X'))
540  goto Catch;
541 
542  StrPtr++;
543  Param->InterpWidth = (int)floor(Number + 0.5);
544 
545  if(!ParseNumber(&Number, &StrPtr, 0))
546  goto Catch;
547 
548  Param->InterpHeight = (int)floor(Number + 0.5);
549  Param->ScaleX = (double)Param->InterpWidth / (double)InputWidth;
550  Param->ScaleY = (double)Param->InterpHeight / (double)InputHeight;
551  EatWhitespace(&StrPtr);
552 
553  switch(*StrPtr)
554  {
555  case '\0': /* <width>x<height> Max size given, preserve aspect ratio */
556  if(InputHeight*Param->InterpWidth
557  <= InputWidth*Param->InterpHeight)
558  {
559  Param->ScaleY = Param->ScaleX;
560  Param->InterpHeight =
561  (int)floor(Param->ScaleY*InputHeight + 0.5);
562 #if VERBOSE > 0
563  printf("Scaling %dx%d to %dx(%d) (preserving aspect ratio)\n",
564  InputWidth, InputHeight,
565  Param->InterpWidth, Param->InterpHeight);
566 #endif
567  }
568  else
569  {
570  Param->ScaleX = Param->ScaleY;
571  Param->InterpWidth =
572  (int)floor(Param->ScaleX*InputWidth + 0.5);
573 #if VERBOSE > 0
574  printf("Scaling %dx%d to (%d)x%d (preserving aspect ratio)\n",
575  InputWidth, InputHeight,
576  Param->InterpWidth, Param->InterpHeight);
577 #endif
578  }
579  break;
580  case '^': /* <width>x<height>^ Min size given, preserve aspect ratio */
581  if(InputHeight*Param->InterpWidth
582  >= InputWidth*Param->InterpHeight)
583  {
584  Param->ScaleY = Param->ScaleX;
585  Param->InterpHeight =
586  (int)floor(Param->ScaleY*InputHeight + 0.5);
587 #if VERBOSE > 0
588  printf("Scaling %dx%d to %dx(%d)^ (preserving aspect ratio)\n",
589  InputWidth, InputHeight,
590  Param->InterpWidth, Param->InterpHeight);
591 #endif
592  }
593  else
594  {
595  Param->ScaleX = Param->ScaleY;
596  Param->InterpWidth =
597  (int)floor(Param->ScaleX*InputWidth + 0.5);
598 #if VERBOSE > 0
599  printf("Scaling %dx%d to (%d)x%d^ (preserving aspect ratio)\n",
600  InputWidth, InputHeight,
601  Param->InterpWidth, Param->InterpHeight);
602 #endif
603  }
604  break;
605  case '!': /* <width>x<height>! Actual size given */
606 #if VERBOSE > 0
607  printf("Scaling %dx%d to %dx%d!\n",
608  InputWidth, InputHeight,
609  Param->InterpWidth, Param->InterpHeight);
610 #endif
611  break;
612  default:
613  goto Catch;
614  }
615  }
616  else
617  goto Catch;
618 
619  return 1;
620 Catch:
621  ErrorMessage("Invalid scaling option \"%s\".\n", Param->ScaleStr);
622  return 0;
623 }