Linear Methods for Image Interpolation
linterpcli.c
Go to the documentation of this file.
00001 
00021 #include <string.h>
00022 #include <ctype.h>
00023 #include <math.h>
00024 
00025 #include "imageio.h"
00026 #include "linterp.h"
00027 #include "strutil.h"
00028 
00029 /* Set to 1 for verbose program output */
00030 #define VERBOSE 0
00031 
00032 
00034 typedef struct
00035 {
00037     float *Data;
00039     int Width;
00041     int Height;
00042 } imagef;
00043 
00044 
00046 typedef struct
00047 {
00049     char *InputFile;
00051     char *OutputFile;
00053     int JpegQuality;
00055     int CenteredGrid;
00057     boundaryhandling Boundary;
00059     char *ScaleStr;
00061     double ScaleX;
00063     double ScaleY;
00065     int InterpWidth;
00067     int InterpHeight;
00069     float Rotation;
00071     char *Method;
00073     float PsfSigma;
00074 } programparams;
00075 
00076 
00077 static int ParseParams(programparams *Param, int argc, char *argv[]);
00078 static int ParseScaling(programparams *Param, int InputWidth, int InputHeight);
00079 
00080 static void PrintHelpMessage()
00081 {
00082     printf("Linear interpolation demo, P. Getreuer 2010-2011\n\n");
00083     printf("Usage: linterp [options] <input file> <output file>\n\n"
00084         "Only " READIMAGE_FORMATS_SUPPORTED " images are supported.\n\n");
00085     printf("Options:\n");
00086     printf("   -m <method>  interpolation method to apply, choices for <method> are\n");
00087     printf("                nearest      nearest neighbor (pixel duplication)\n");
00088     printf("                bilinear     standard bilinear interpolation\n");
00089     printf("                bicubic      Keys bicubic with parameter -0.5\n");
00090     printf("                lanczosN     Lanczos radius-N sinc approximation,\n");
00091     printf("                             N = 2, 3, 4\n");
00092     printf("                bsplineN     B-spline of degree N,\n");
00093     printf("                             N = 2, 3, 5, 7, 9, 11\n");
00094     printf("                omomsN       o-Moms of degree N,\n");
00095     printf("                             N = 3, 5, 7\n");
00096     printf("                fourier      Fourier zero-padding (sinc)\n\n");
00097     printf("   -x <scale>              the scale factor (may be non-integer)\n");
00098     printf("   -x <x-scale>,<y-scale>  set horizontal and vertical scale factors\n");
00099     printf("   -x <width>x<height>     set maximum interpolated size in pixels, \n");
00100     printf("                           preserves aspect ratio\n");
00101     printf("   -x <width>x<height>^    set minimum interpolated size in pixels, \n");
00102     printf("                           preserves aspect ratio\n");
00103     printf("   -x <width>x<height>!    set actual interpolated size in pixels, \n");
00104     printf("                           ignores aspect ratio\n\n");
00105     printf("   -r <number>  rotation, counter clockwise in degrees\n");
00106     printf("                (if specified, preserves aspect ratio regardless of -x)\n");
00107     printf("   -p <number>  sigma_h, the blur size of the point spread function\n");
00108     printf("   -b <ext>     extension to use for boundary handling, choices for <ext> are\n");
00109     printf("                const        constant extension\n");
00110     printf("                hsym         half-sample symmetric\n");
00111     printf("                wsym         whole-sample symmetric\n");
00112     printf("   -g <grid>    grid to use for resampling, choices for <grid> are\n"
00113            "                centered     grid with centered alignment (default)\n"
00114            "                topleft      the top-left anchored grid\n\n");
00115 #ifdef LIBJPEG_SUPPORT
00116     printf("   -q <number>  quality for saving JPEG images (0 to 100)\n\n");
00117 #endif
00118     printf("Example: 4.5x cubic B-spline scaling, sigma_h = 0.35\n"
00119         "   linterp -m bspline3 -x 4.5 -p 0.35 frog.bmp interpolation.bmp\n");
00120 }
00121 
00122 
00123 float GaussianPsf(float x, const void *Param)
00124 {
00125     float Sigma = *((float *)Param);
00126     return exp(-(x*x)/(2*Sigma*Sigma)) / (sqrt(M_2PI)*Sigma);
00127 }
00128 
00129 
00130 imagef ScaleRotateImage(imagef v, programparams Param)
00131 {
00132     interpmethod *Method;
00133     const int InputNumPixels = v.Width*v.Height;
00134     imagef u = {NULL, 0, 0};
00135     float *Coeff = NULL, *X = NULL, *Y = NULL;
00136     float XStart, XStep, YStart, YStep, Theta;
00137     int NumCoeffs, Channel;
00138 
00139 
00140     if(!(Method = GetInterpMethod(Param.Method)))
00141     {
00142         fprintf(stderr, "Unknown interpolation method.\n");
00143         goto Catch;
00144     }
00145 
00146     Theta = Param.Rotation*M_PI/180;
00147 
00148     /* Prefilter the image if necessary */
00149     if(Param.PsfSigma > 0)
00150     {
00151         if(Param.Boundary != BOUNDARY_HSYMMETRIC
00152             && Param.Boundary != BOUNDARY_WSYMMETRIC)
00153         {
00154             fprintf(stderr,
00155 "PSF prefiltering only supported for half- and whole-sample symmetric\n"
00156 "boundary extension.\n");
00157             goto Catch;
00158         }
00159 
00160         NumCoeffs = 2 + ceil(Method->KernelRadius + 5*Param.PsfSigma);
00161 
00162         if(!(Coeff = (float *)Malloc(sizeof(float)*NumCoeffs)))
00163         {
00164             fprintf(stderr, "Memory allocation failed.\n");
00165             goto Catch;
00166         }
00167 
00168 #if VERBOSE > 0
00169         printf("PSF prefiltering\n");
00170 #endif
00171         PsfConvCoeff(Coeff, NumCoeffs, GaussianPsf,
00172             (const void *)&Param.PsfSigma, Method->Kernel,
00173             Method->KernelRadius, Method->KernelNormalize);
00174         PsfPreFilter(v.Data, v.Width, v.Height, 3,
00175             Coeff, NumCoeffs, Param.Boundary);
00176     }
00177     else if(Method->PrefilterNumAlpha)
00178     {
00179 #if VERBOSE > 0
00180         printf("Prefiltering\n");
00181 #endif
00182         PrefilterImage(v.Data, v.Width, v.Height, 3, Method->PrefilterAlpha,
00183             Method->PrefilterNumAlpha, Method->PrefilterScale, Param.Boundary);
00184     }
00185 
00186     if(Theta == 0)  /* Scaling without rotation */
00187     {
00188         u.Width = Param.InterpWidth;
00189         u.Height = Param.InterpHeight;
00190 
00191 #if VERBOSE > 0
00192         printf("Scaling %dx%d -> %dx%d\n",
00193             v.Width, v.Height, u.Width, u.Height);
00194 #endif
00195 
00196         if(!(u.Data = (float *)Malloc(sizeof(float)*3*u.Width*u.Height)))
00197             goto Catch;
00198 
00199         XStep = 1.0/Param.ScaleX;
00200         YStep = 1.0/Param.ScaleY;
00201 
00202         if(Param.CenteredGrid)
00203         {
00204             XStart = (XStep - 1.0)/2;
00205             YStart = (YStep - 1.0)/2;
00206         }
00207         else
00208             XStart = YStart = 0;
00209 
00210         if(!LinScale2d(u.Data, u.Width, XStart, XStep, u.Height, YStart, YStep,
00211             v.Data, v.Width, v.Height, 3, Method->Kernel, Method->KernelRadius,
00212             Method->KernelNormalize, Param.Boundary))
00213             goto Catch;
00214     }
00215     else        /* Scaling and rotation */
00216     {
00217         /* Create a list of locations (X[n],Y[n]) that sample a grid of size
00218            u.Width by u.Height rotated by Theta and scaled by factor Scale.
00219 
00220            For other interpolation operations like a perspective transformation,
00221            rewrite this step so that (X[n],Y[n]) is the desired nth sampling
00222            location and set u.Width and u.Height accordingly. */
00223         if(!MakeScaleRotationGrid(&X, &Y, &u.Width, &u.Height,
00224             v.Width, v.Height, (Param.ScaleX + Param.ScaleY)/2, Theta))
00225             goto Catch;
00226 
00227 #if VERBOSE > 0
00228         printf("Scaling and rotating %dx%d -> %dx%d\n",
00229             v.Width, v.Height, u.Width, u.Height);
00230 #endif
00231 
00232         if(!(u.Data = (float *)Malloc(sizeof(float)*3*u.Width*u.Height)))
00233             goto Catch;
00234 
00235         for(Channel = 0; Channel < 3; Channel++)
00236             if(!LinInterp2d(u.Data + Channel*u.Width*u.Height,
00237                 v.Data + Channel*InputNumPixels, v.Width, v.Height,
00238                 X, Y, u.Width*u.Height, Method->Kernel,
00239                 Method->KernelRadius, Method->KernelNormalize, Param.Boundary))
00240                 goto Catch;
00241 
00242         Free(Y);
00243         Free(X);
00244     }
00245 
00246     Free(Coeff);
00247     return u;
00248 Catch:
00249     Free(u.Data);
00250     Free(Y);
00251     Free(X);
00252     Free(Coeff);
00253     u.Data = 0;
00254     u.Width = u.Height = 0;
00255     return u;
00256 }
00257 
00258 
00259 int main(int argc, char *argv[])
00260 {
00261     programparams Param;
00262     imagef v = {NULL, 0, 0}, u = {NULL, 0, 0};
00263     unsigned long StartTime;
00264     float XStart, YStart;
00265 
00266 
00267     if(!ParseParams(&Param, argc, argv))
00268         return 0;
00269 
00270     /* Read the input image */
00271     if(!(v.Data = (float *)ReadImage(&v.Width, &v.Height, Param.InputFile,
00272          IMAGEIO_FLOAT | IMAGEIO_RGB | IMAGEIO_PLANAR)))
00273         return 1;
00274     else if(v.Width < 3 || v.Height < 3)
00275     {
00276         fprintf(stderr, "Image is too small (%dx%d).\n", v.Width, v.Height);
00277         goto Catch;
00278     }
00279 
00280     if(!ParseScaling(&Param, v.Width, v.Height))
00281         goto Catch;
00282 
00283     StartTime = Clock();
00284 
00285     if(!strcmp(Param.Method, "fourier") || !strcmp(Param.Method, "sinc"))
00286     {
00287         if(Param.Rotation != 0)
00288         {
00289             fprintf(stderr, "Rotation is not supported with Fourier interpolation.\n");
00290             goto Catch;
00291         }
00292         else if(Param.Boundary != BOUNDARY_HSYMMETRIC
00293             && Param.Boundary != BOUNDARY_WSYMMETRIC)
00294         {
00295             fprintf(stderr,
00296 "Fourier interpolation is only supported for half- and whole-sample\n"
00297 "symmetric boundary extension.\n");
00298             goto Catch;
00299         }
00300 
00301         u.Width = Param.InterpWidth;
00302         u.Height = Param.InterpHeight;
00303 
00304 #if VERBOSE > 0
00305         printf("Fourier scaling %dx%d -> %dx%d\n",
00306                 v.Width, v.Height, u.Width, u.Height);
00307 #endif
00308 
00309         if(!(u.Data = (float *)Malloc(sizeof(float)*3*u.Width*u.Height)))
00310             goto Catch;
00311 
00312         if(Param.CenteredGrid)
00313         {
00314             XStart = v.Width/(2.0f*u.Width) - 0.5f;
00315             YStart = v.Height/(2.0f*u.Height) - 0.5f;
00316         }
00317         else
00318             XStart = YStart = 0;
00319 
00320         if(!FourierScale2d(u.Data, u.Width, XStart, u.Height, YStart,
00321             v.Data, v.Width, v.Height, 3, Param.PsfSigma, Param.Boundary))
00322             goto Catch;
00323     }
00324     else
00325         u = ScaleRotateImage(v, Param);
00326 
00327     if(!u.Data)
00328     {
00329         fprintf(stderr, "Error in computation.\n");
00330         goto Catch;
00331     }
00332 
00333     printf("CPU Time: %.3f\n", (Clock() - StartTime)*0.001f);
00334 
00335     /* Write output */
00336     if(!WriteImage(u.Data, u.Width, u.Height, Param.OutputFile,
00337         IMAGEIO_FLOAT | IMAGEIO_RGB | IMAGEIO_PLANAR, Param.JpegQuality))
00338         fprintf(stderr, "Error writing output file.\n");
00339 
00340 Catch:
00341     Free(u.Data);
00342     Free(v.Data);
00343     return 0;
00344 }
00345 
00346 
00347 static int ParseParams(programparams *Param, int argc, char *argv[])
00348 {
00349     static char *DefaultOutputFile = (char *)"out.bmp";
00350     char *OptionString;
00351     char OptionChar;
00352     int i;
00353 
00354 
00355     if(argc < 2)
00356     {
00357         PrintHelpMessage();
00358         return 0;
00359     }
00360 
00361     /* Set parameter defaults */
00362     Param->InputFile = 0;
00363     Param->OutputFile = DefaultOutputFile;
00364     Param->CenteredGrid = 1;
00365     Param->Boundary = BOUNDARY_HSYMMETRIC;
00366     Param->ScaleStr = (char *)"1";
00367     Param->Rotation = 0;
00368     Param->Method = (char *)"bspline3";
00369     Param->PsfSigma = 0;
00370     Param->JpegQuality = 80;
00371 
00372     for(i = 1; i < argc;)
00373     {
00374         if(argv[i] && argv[i][0] == '-')
00375         {
00376             if((OptionChar = argv[i][1]) == 0)
00377             {
00378                 fprintf(stderr, "Invalid parameter format.\n");
00379                 return 0;
00380             }
00381 
00382             if(argv[i][2])
00383                 OptionString = &argv[i][2];
00384             else if(++i < argc)
00385                 OptionString = argv[i];
00386             else
00387             {
00388                 fprintf(stderr, "Invalid parameter format.\n");
00389                 return 0;
00390             }
00391 
00392             switch(OptionChar)
00393             {
00394             case 'g':
00395                 if(!strcmp(OptionString, "centered")
00396                     || !strcmp(OptionString, "center"))
00397                     Param->CenteredGrid = 1;
00398                 else if(!strcmp(OptionString, "topleft")
00399                     || !strcmp(OptionString, "top-left"))
00400                     Param->CenteredGrid = 0;
00401                 else
00402                 {
00403                     fprintf(stderr, "Grid must be either \"centered\" or \"topleft\".\n");
00404                     return 0;
00405                 }
00406                 break;
00407             case 'b':
00408                 if(!strcmp(OptionString, "const"))
00409                     Param->Boundary = BOUNDARY_CONSTANT;
00410                 else if(!strcmp(OptionString, "hsym"))
00411                     Param->Boundary = BOUNDARY_HSYMMETRIC;
00412                 else if(!strcmp(OptionString, "wsym"))
00413                     Param->Boundary = BOUNDARY_WSYMMETRIC;
00414                 else
00415                 {
00416                     fprintf(stderr, "Invalid boundary extension.\n");
00417                     return 0;
00418                 }
00419                 break;
00420             case 'x':
00421                 Param->ScaleStr = OptionString;
00422                 break;
00423             case 'r':
00424                 Param->Rotation = atof(OptionString);
00425                 break;
00426             case 'm':
00427                 Param->Method = OptionString;
00428                 break;
00429             case 'p':
00430                 Param->PsfSigma = atof(OptionString);
00431 
00432                 if(Param->PsfSigma < 0.0)
00433                 {
00434                     fprintf(stderr, "Point spread blur size must be nonnegative.\n");
00435                     return 0;
00436                 }
00437                 break;
00438 #ifdef LIBJPEG_SUPPORT
00439             case 'q':
00440                 Param->JpegQuality = atoi(OptionString);
00441 
00442                 if(Param->JpegQuality <= 0 || Param->JpegQuality > 100)
00443                 {
00444                     fprintf(stderr, "JPEG quality must be between 0 and 100.\n");
00445                     return 0;
00446                 }
00447                 break;
00448 #endif
00449             case '-':
00450                 PrintHelpMessage();
00451                 return 0;
00452             default:
00453                 if(isprint(OptionChar))
00454                     fprintf(stderr, "Unknown option \"-%c\".\n", OptionChar);
00455                 else
00456                     fprintf(stderr, "Unknown option.\n");
00457 
00458                 return 0;
00459             }
00460 
00461             i++;
00462         }
00463         else
00464         {
00465             if(!Param->InputFile)
00466                 Param->InputFile = argv[i];
00467             else
00468                 Param->OutputFile = argv[i];
00469 
00470             i++;
00471         }
00472     }
00473 
00474     if(!Param->InputFile)
00475     {
00476         PrintHelpMessage();
00477         return 0;
00478     }
00479 
00480     /* Display the chosen parameters */
00481     printf("Interpolation with %s\n", Param->Method);
00482     return 1;
00483 }
00484 
00485 
00486 /*
00487  * Parse the scaling option string
00488  *
00489  * Syntax
00490  * <scale>              Scale by factor <scale>
00491  * <scalex>,<scaley>    Scale width and height individually
00492  * <width>x<height>     Max size given, aspect ratio preserved
00493  * <width>x<height>^    Min size given, aspect ratio preserved
00494  * <width>x<height>!    Actual size given, aspect ratio ignored
00495  */
00496 static int ParseScaling(programparams *Param, int InputWidth, int InputHeight)
00497 {
00498     const char *StrPtr = Param->ScaleStr;
00499     double Number;
00500 
00501     if(!ParseNumber(&Number, &StrPtr, 1))
00502         goto Catch;
00503 
00504     if(!EatWhitespace(&StrPtr))
00505     {   /* Syntax <scale> */
00506         Param->ScaleX = Param->ScaleY = Number;
00507         Param->InterpWidth = (int)ceil(Param->ScaleX*InputWidth);
00508         Param->InterpHeight = (int)ceil(Param->ScaleY*InputHeight);
00509 #if VERBOSE > 0
00510         printf("Scaling by %g (%dx%d to %dx%d)\n", Param->ScaleX,
00511             InputWidth, InputHeight,
00512             Param->InterpWidth, Param->InterpHeight);
00513 #endif
00514     }
00515     else if(*StrPtr == ',')
00516     {   /* Syntax <scalex>,<scaley> */
00517         StrPtr++;
00518         Param->ScaleX = Number;
00519 
00520         if(!ParseNumber(&Number, &StrPtr, 1))
00521             goto Catch;
00522 
00523         Param->ScaleY = Number;
00524         Param->InterpWidth = (int)ceil(Param->ScaleX*InputWidth);
00525         Param->InterpHeight = (int)ceil(Param->ScaleY*InputHeight);
00526 #if VERBOSE > 0
00527         printf("Scaling by %g,%g (%dx%d to %dx%d)\n",
00528             Param->ScaleX, Param->ScaleY,
00529             InputWidth, InputHeight,
00530             Param->InterpWidth, Param->InterpHeight);
00531 #endif
00532     }
00533     else if(*StrPtr == 'x' || *StrPtr == 'X')
00534     {   /* Syntax <width>x<height>... */
00535         StrPtr = Param->ScaleStr;
00536 
00537         /* Reparse as integer */
00538         if(!ParseNumber(&Number, &StrPtr, 0)
00539             || !(*StrPtr == 'x' || *StrPtr == 'X'))
00540             goto Catch;
00541 
00542         StrPtr++;
00543         Param->InterpWidth = (int)floor(Number + 0.5);
00544 
00545         if(!ParseNumber(&Number, &StrPtr, 0))
00546             goto Catch;
00547 
00548         Param->InterpHeight = (int)floor(Number + 0.5);
00549         Param->ScaleX = (double)Param->InterpWidth / (double)InputWidth;
00550         Param->ScaleY = (double)Param->InterpHeight / (double)InputHeight;
00551         EatWhitespace(&StrPtr);
00552 
00553         switch(*StrPtr)
00554         {
00555         case '\0': /* <width>x<height> Max size given, preserve aspect ratio */
00556             if(InputHeight*Param->InterpWidth
00557                 <= InputWidth*Param->InterpHeight)
00558             {
00559                 Param->ScaleY = Param->ScaleX;
00560                 Param->InterpHeight =
00561                     (int)floor(Param->ScaleY*InputHeight + 0.5);
00562 #if VERBOSE > 0
00563                 printf("Scaling %dx%d to %dx(%d) (preserving aspect ratio)\n",
00564                     InputWidth, InputHeight,
00565                     Param->InterpWidth, Param->InterpHeight);
00566 #endif
00567             }
00568             else
00569             {
00570                 Param->ScaleX = Param->ScaleY;
00571                 Param->InterpWidth =
00572                     (int)floor(Param->ScaleX*InputWidth + 0.5);
00573 #if VERBOSE > 0
00574                 printf("Scaling %dx%d to (%d)x%d (preserving aspect ratio)\n",
00575                     InputWidth, InputHeight,
00576                     Param->InterpWidth, Param->InterpHeight);
00577 #endif
00578             }
00579             break;
00580         case '^': /* <width>x<height>^ Min size given, preserve aspect ratio */
00581             if(InputHeight*Param->InterpWidth
00582                 >= InputWidth*Param->InterpHeight)
00583             {
00584                 Param->ScaleY = Param->ScaleX;
00585                 Param->InterpHeight =
00586                     (int)floor(Param->ScaleY*InputHeight + 0.5);
00587 #if VERBOSE > 0
00588                 printf("Scaling %dx%d to %dx(%d)^ (preserving aspect ratio)\n",
00589                     InputWidth, InputHeight,
00590                     Param->InterpWidth, Param->InterpHeight);
00591 #endif
00592             }
00593             else
00594             {
00595                 Param->ScaleX = Param->ScaleY;
00596                 Param->InterpWidth =
00597                     (int)floor(Param->ScaleX*InputWidth + 0.5);
00598 #if VERBOSE > 0
00599                 printf("Scaling %dx%d to (%d)x%d^ (preserving aspect ratio)\n",
00600                     InputWidth, InputHeight,
00601                     Param->InterpWidth, Param->InterpHeight);
00602 #endif
00603             }
00604             break;
00605         case '!': /* <width>x<height>! Actual size given */
00606 #if VERBOSE > 0
00607             printf("Scaling %dx%d to %dx%d!\n",
00608                     InputWidth, InputHeight,
00609                     Param->InterpWidth, Param->InterpHeight);
00610 #endif
00611             break;
00612         default:
00613             goto Catch;
00614         }
00615     }
00616     else
00617         goto Catch;
00618 
00619     return 1;
00620 Catch:
00621     ErrorMessage("Invalid scaling option \"%s\".\n", Param->ScaleStr);
00622     return 0;
00623 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines