Linear Methods for Image Interpolation
|
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 }