Linear Methods for Image Interpolation
imcoarsen.c
Go to the documentation of this file.
00001 
00020 #include <math.h>
00021 #include <string.h>
00022 #include <ctype.h>
00023 
00024 #include "imageio.h"
00025 #include "strutil.h"
00026 
00027 #define VERBOSE 0
00028 
00030 #define NUMSTDS 4
00031 
00032 
00034 typedef struct
00035 {
00037     uint32_t *Data;
00039     int Width;
00041     int Height;
00042 } image;
00043 
00044 typedef enum
00045 {
00046     BOUNDARY_CONSTANT = 0,
00047     BOUNDARY_HSYMMETRIC = 1,
00048     BOUNDARY_WSYMMETRIC = 2,
00049     BOUNDARY_PERIODIC = 3
00050 } boundaryhandling;
00051 
00053 typedef struct
00054 {
00056     char *InputFile;
00058     char *OutputFile;
00060     int JpegQuality;
00062     int CenteredGrid;
00064     boundaryhandling Boundary;
00066     char *ScaleStr;
00068     double ScaleX;
00070     double ScaleY;
00072     int CoarseWidth;
00074     int CoarseHeight;
00076     float PsfSigma;
00077 } programparams;
00078 
00079 
00080 int ParseParams(programparams *Param, int argc, char *argv[]);
00081 static int ParseScaling(programparams *Param, int InputWidth, int InputHeight);
00082 int Coarsen(image v, image u, programparams Param);
00083 
00085 void PrintHelpMessage()
00086 {
00087     printf("Image coarsening utility, P. Getreuer 2010-2011\n\n");
00088     printf("Usage: imcoarsen [options] <input file> <output file>\n\n"
00089             "Only " READIMAGE_FORMATS_SUPPORTED " images are supported.\n\n");
00090     printf("Options:\n");
00091     printf("   -x <number>             coarsening factor (>= 1.0, may be non-integer)\n");
00092     printf("   -x <x-scale>,<y-scale>  set horizontal and vertical coarsening factors\n");
00093     printf("   -x <width>x<height>     set maximum coarsened size in pixels, \n");
00094     printf("                           preserves aspect ratio\n");
00095     printf("   -x <width>x<height>^    set minimum coarsened size in pixels, \n");
00096     printf("                           preserves aspect ratio\n");
00097     printf("   -x <width>x<height>!    set actual coarsened size in pixels, \n");
00098     printf("                           ignores aspect ratio\n\n");
00099     
00100     printf("   -p <number>  sigma_h, the blur size of the Gaussian point spread function\n"
00101            "                in units of output pixels.\n");
00102     printf("   -b <ext>     extension to use for boundary handling, choices for <ext> are\n");
00103     printf("                const        constant extension\n");
00104     printf("                hsym         half-sample symmetric (default)\n");
00105     printf("                wsym         whole-sample symmetric\n");
00106     printf("                per          periodic\n");
00107     printf("   -g <grid>    grid to use for resampling, choices for <grid> are\n"
00108            "                centered     grid with centered alignment (default)\n"
00109            "                topleft      the top-left anchored grid\n\n");
00110 #ifdef LIBJPEG_SUPPORT
00111     printf("   -q <number>  quality for saving JPEG images (0 to 100)\n\n");
00112 #endif
00113     printf("Example: coarsen by factor 2.5\n"
00114         "   imcoarsen -x 2.5 -p 0.35 frog.bmp coarse.bmp\n");
00115 }
00116 
00117 
00118 int main(int argc, char *argv[])
00119 {
00120     programparams Param;
00121     image u = {NULL, 0, 0}, v = {NULL, 0, 0};
00122     int Status = 1;
00123 
00124 
00125     if(!ParseParams(&Param, argc, argv))
00126         return 0;
00127 
00128     /* Read the input image */
00129     if(!(u.Data = (uint32_t *)ReadImage(&u.Width, &u.Height, Param.InputFile,
00130         IMAGEIO_U8 | IMAGEIO_RGBA)))
00131         goto Catch;
00132     
00133     if(!ParseScaling(&Param, u.Width, u.Height))
00134         goto Catch;
00135 
00136     if(Param.ScaleX >= u.Width || Param.ScaleY >= u.Height)
00137     {
00138         ErrorMessage("Image is too small for scale factor.\n");
00139         goto Catch;
00140     }
00141 
00142     /* Allocate the output image */
00143     v.Width = Param.CoarseWidth;
00144     v.Height = Param.CoarseHeight;
00145     
00146 #if VERBOSE > 0
00147     printf("%dx%d input -> %dx%d output\n", u.Width, u.Height, v.Width, v.Height);
00148 #endif
00149 
00150     if(!(v.Data = (uint32_t *)Malloc(sizeof(uint32_t)*
00151         ((long int)v.Width)*((long int)v.Height))))
00152         goto Catch;
00153 
00154     /* Convolution followed by downsampling */
00155     if(!Coarsen(v, u, Param))
00156         goto Catch;
00157 
00158     /* Write the output image */
00159     if(!WriteImage(v.Data, v.Width, v.Height, Param.OutputFile,
00160         IMAGEIO_U8 | IMAGEIO_RGBA, Param.JpegQuality))
00161         goto Catch;
00162 #if VERBOSE > 0
00163     else
00164         printf("Output written to \"%s\".\n", Param.OutputFile);
00165 #endif
00166 
00167     Status = 0; /* Finished successfully, set exit status to zero. */
00168 
00169 Catch:
00170     Free(v.Data);
00171     Free(u.Data);
00172     return Status;
00173 }
00174 
00175 
00176 float Sqr(float x)
00177 {
00178     return x*x;
00179 }
00180 
00187 static int ConstExtension(int N, int i)
00188 {
00189     if(i < 0)
00190         return 0;
00191     else if(i >= N)
00192         return N - 1;
00193     else
00194         return i;
00195 }
00196 
00197 
00204 static int HSymExtension(int N, int i)
00205 {
00206     while(1)
00207     {
00208         if(i < 0)
00209             i = -1 - i;
00210         else if(i >= N)
00211             i = (2*N - 1) - i;
00212         else
00213             return i;
00214     }
00215 }
00216 
00217 
00224 static int WSymExtension(int N, int i)
00225 {
00226     while(1)
00227     {
00228         if(i < 0)
00229             i = -i;
00230         else if(i >= N)
00231             i = (2*N - 2) - i;
00232         else
00233             return i;
00234     }
00235 }
00236 
00237 
00244 static int PerExtension(int N, int i)
00245 {
00246     while(1)
00247     {
00248         if(i < 0)
00249             i += N;
00250         else if(i >= N)
00251             i -= N;
00252         else
00253             return i;
00254     }
00255 }
00256 
00257 
00258 int (*ExtensionMethod[4])(int, int) =
00259     {ConstExtension, HSymExtension, WSymExtension, PerExtension};
00260 
00261 
00262 int Coarsen(image v, image u, programparams Param)
00263 {
00264     int (*Extension)(int, int) = ExtensionMethod[Param.Boundary];
00265     const float PsfRadiusX = NUMSTDS*Param.PsfSigma*Param.ScaleX;
00266     const float PsfRadiusY = NUMSTDS*Param.PsfSigma*Param.ScaleY;
00267     const int PsfWidth = 1 + ((PsfRadiusX == 0) ? 1 : (int)ceil(2*PsfRadiusX));
00268     const int PsfHeight = 1 + ((PsfRadiusY == 0) ? 1 : (int)ceil(2*PsfRadiusY));
00269     float *Temp = NULL, *PsfBuf = NULL;
00270     float ExpDenomX, ExpDenomY, Weight, Sum[4], DenomSum;
00271     float XStart, YStart, X, Y;
00272     uint32_t Pixel;
00273     int IndexX0, IndexY0, SrcOffset, DestOffset;
00274     int x, y, n, c, Success = 0;
00275 
00276 
00277     if(!(Temp = (float *)Malloc(sizeof(float)*4*v.Width*u.Height))
00278         || !(PsfBuf = (float *)Malloc(sizeof(float)
00279             *((PsfWidth >= PsfHeight) ? PsfWidth : PsfHeight))))
00280         goto Catch;
00281 
00282     ExpDenomX = 2 * Sqr(Param.PsfSigma*Param.ScaleX);
00283     ExpDenomY = 2 * Sqr(Param.PsfSigma*Param.ScaleY);
00284 
00285     if(Param.CenteredGrid)
00286     {
00287         XStart = (1/Param.ScaleX - 1)/2;
00288         YStart = (1/Param.ScaleY - 1)/2;
00289     }
00290     else
00291         XStart = YStart = 0;
00292 
00293     for(x = 0; x < v.Width; x++)
00294     {
00295         X = (-XStart + x)*Param.ScaleX;
00296         IndexX0 = (int)ceil(X - PsfRadiusX - 0.5f);
00297         DenomSum = 0;
00298 
00299         /* Evaluate the PSF */
00300         for(n = 0; n < PsfWidth; n++)
00301         {
00302             PsfBuf[n] = Sqr(X - (IndexX0 + n));
00303 
00304             if(!n || PsfBuf[n] < DenomSum)
00305                 DenomSum = PsfBuf[n];
00306         }
00307 
00308         if(ExpDenomX > 0)
00309             for(n = 0; n < PsfWidth; n++)
00310                 PsfBuf[n] = (float)exp((DenomSum - PsfBuf[n]) / ExpDenomX);
00311         else if(IndexX0 == (int)floor(X - PsfRadiusX + 0.5f))
00312         {   /* If PsfSigma = 0, sample the nearest neighbor */
00313             PsfBuf[0] = 1;
00314             PsfBuf[1] = 0;
00315         }
00316         else /* At a half integer, average the two nearest neighbors */
00317             PsfBuf[0] = PsfBuf[1] = 0.5f;
00318 
00319         DenomSum = 0;
00320 
00321         for(n = 0; n < PsfWidth; n++)
00322             DenomSum += PsfBuf[n];
00323 
00324         for(y = 0, SrcOffset = 0, DestOffset = x; y < u.Height;
00325             y++, SrcOffset += u.Width, DestOffset += v.Width)
00326         {
00327             Sum[0] = Sum[1] = Sum[2] = Sum[3] = 0;
00328 
00329             for(n = 0; n < PsfWidth; n++)
00330             {
00331                 Weight = PsfBuf[n];
00332                 Pixel = u.Data[Extension(u.Width, IndexX0 + n) + SrcOffset];
00333 
00334                 for(c = 0; c < 4; c++)
00335                     Sum[c] += (float)((uint8_t *)&Pixel)[c] * Weight;
00336             }
00337 
00338             for(c = 0; c < 4; c++)
00339                 Temp[4*DestOffset + c] = Sum[c] / DenomSum;
00340         }
00341     }
00342 
00343     for(y = 0; y < v.Height; y++, v.Data += v.Width)
00344     {
00345         Y = (-YStart + y)*Param.ScaleY;
00346         IndexY0 = (int)ceil(Y - PsfRadiusY - 0.5f);
00347         DenomSum = 0;
00348 
00349         /* Evaluate the PSF */
00350         for(n = 0; n < PsfHeight; n++)
00351         {
00352             PsfBuf[n] = Sqr(Y - (IndexY0 + n));
00353 
00354             if(!n || PsfBuf[n] < DenomSum)
00355                 DenomSum = PsfBuf[n];
00356         }
00357 
00358         if(ExpDenomY > 0)
00359             for(n = 0; n < PsfHeight; n++)
00360                 PsfBuf[n] = (float)exp((DenomSum - PsfBuf[n]) / ExpDenomY);
00361         else if(IndexY0 == (int)floor(Y - PsfRadiusY + 0.5f))
00362         {   /* If PsfSigma = 0, sample the nearest neighbor */
00363             PsfBuf[0] = 1;
00364             PsfBuf[1] = 0;
00365         }
00366         else /* At a half integer, average the two nearest neighbors */
00367             PsfBuf[0] = PsfBuf[1] = 0.5f;
00368 
00369         DenomSum = 0;
00370 
00371         for(n = 0; n < PsfHeight; n++)
00372             DenomSum += PsfBuf[n];
00373 
00374         for(x = 0; x < v.Width; x++)
00375         {
00376             Sum[0] = Sum[1] = Sum[2] = Sum[3] = 0;
00377 
00378             for(n = 0; n < PsfHeight; n++)
00379             {
00380                 SrcOffset = x + v.Width*Extension(u.Height, IndexY0 + n);
00381                 Weight = PsfBuf[n];
00382 
00383                 for(c = 0; c < 4; c++)
00384                     Sum[c] += Temp[4*SrcOffset + c] * Weight;
00385             }
00386 
00387             for(c = 0; c < 4; c++)
00388                 ((uint8_t *)&Pixel)[c] = (int)(Sum[c] / DenomSum + 0.5f);
00389 
00390             v.Data[x] = Pixel;
00391         }
00392     }
00393 
00394     Success = 1;
00395 Catch:
00396     Free(PsfBuf);
00397     Free(Temp);
00398     return Success;
00399 }
00400 
00401 
00402 /*
00403  * Parse the scaling option string
00404  *
00405  * Syntax
00406  * <scale>              Scale by factor <scale>
00407  * <scalex>,<scaley>    Scale width and height individually
00408  * <width>x<height>     Max size given, aspect ratio preserved
00409  * <width>x<height>^    Min size given, aspect ratio preserved
00410  * <width>x<height>!    Actual size given, aspect ratio ignored
00411  */
00412 static int ParseScaling(programparams *Param, int InputWidth, int InputHeight)
00413 {
00414     const char *StrPtr = Param->ScaleStr;
00415     double Number;
00416 
00417     if(!ParseNumber(&Number, &StrPtr, 1))
00418         goto Catch;
00419 
00420     if(!EatWhitespace(&StrPtr))
00421     {   /* Syntax <scale> */
00422         Param->ScaleX = Param->ScaleY = Number;
00423         Param->CoarseWidth = (int)ceil(InputWidth/Param->ScaleX);
00424         Param->CoarseHeight = (int)ceil(InputHeight/Param->ScaleY);
00425     }
00426     else if(*StrPtr == ',')
00427     {   /* Syntax <scalex>,<scaley> */
00428         StrPtr++;
00429         Param->ScaleX = Number;
00430 
00431         if(!ParseNumber(&Number, &StrPtr, 1))
00432             goto Catch;
00433 
00434         Param->ScaleY = Number;
00435         Param->CoarseWidth = (int)ceil(InputWidth/Param->ScaleX);
00436         Param->CoarseHeight = (int)ceil(InputHeight/Param->ScaleY);
00437     }
00438     else if(*StrPtr == 'x' || *StrPtr == 'X')
00439     {   /* Syntax <width>x<height>... */
00440         StrPtr = Param->ScaleStr;
00441 
00442         /* Reparse as integer */
00443         if(!ParseNumber(&Number, &StrPtr, 0)
00444             || !(*StrPtr == 'x' || *StrPtr == 'X'))
00445             goto Catch;
00446 
00447         StrPtr++;
00448         Param->CoarseWidth = (int)floor(Number + 0.5);
00449 
00450         if(!ParseNumber(&Number, &StrPtr, 0))
00451             goto Catch;
00452 
00453         Param->CoarseHeight = (int)floor(Number + 0.5);
00454         Param->ScaleX = ((double)InputWidth) / ((double)Param->CoarseWidth);
00455         Param->ScaleY = ((double)InputHeight) / ((double)Param->CoarseHeight);
00456         EatWhitespace(&StrPtr);
00457 
00458         switch(*StrPtr)
00459         {
00460         case '\0': /* <width>x<height> Max size given, preserve aspect ratio */
00461             if(InputHeight*Param->CoarseWidth
00462                 <= InputWidth*Param->CoarseHeight)
00463             {
00464                 Param->ScaleY = Param->ScaleX;
00465                 Param->CoarseHeight =
00466                     (int)floor(InputHeight/Param->ScaleY + 0.5);
00467             }
00468             else
00469             {
00470                 Param->ScaleX = Param->ScaleY;
00471                 Param->CoarseWidth =
00472                     (int)floor(InputWidth/Param->ScaleX + 0.5);
00473             }
00474             break;
00475         case '^': /* <width>x<height>^ Min size given, preserve aspect ratio */
00476             if(InputHeight*Param->CoarseWidth
00477                 >= InputWidth*Param->CoarseHeight)
00478             {
00479                 Param->ScaleY = Param->ScaleX;
00480                 Param->CoarseHeight =
00481                     (int)floor(InputHeight/Param->ScaleY + 0.5);
00482             }
00483             else
00484             {
00485                 Param->ScaleX = Param->ScaleY;
00486                 Param->CoarseWidth =
00487                     (int)floor(InputWidth/Param->ScaleX + 0.5);
00488             }
00489             break;
00490         case '!': /* <width>x<height>! Actual size given */
00491             break;
00492         default:
00493             goto Catch;
00494         }
00495     }
00496     else
00497         goto Catch;
00498 
00499     return 1;
00500 Catch:
00501     ErrorMessage("Invalid scaling option \"%s\".\n", Param->ScaleStr);
00502     return 0;
00503 }
00504 
00505 
00506 int ParseParams(programparams *Param, int argc, char *argv[])
00507 {
00508     static char *DefaultOutputFile = (char *)"out.bmp";
00509     static char *DefaultScaleStr = (char *)"1";
00510     char *OptionString;
00511     char OptionChar;
00512     int i;
00513 
00514 
00515     if(argc < 2)
00516     {
00517         PrintHelpMessage();
00518         return 0;
00519     }
00520 
00521     /* Set parameter defaults */
00522     Param->InputFile = 0;
00523     Param->OutputFile = DefaultOutputFile;
00524     Param->JpegQuality = 99;
00525     Param->ScaleStr = DefaultScaleStr;
00526     Param->PsfSigma = 0.35f;
00527     Param->CenteredGrid = 1;
00528     Param->Boundary = BOUNDARY_HSYMMETRIC;
00529 
00530     for(i = 1; i < argc;)
00531     {
00532         if(argv[i] && argv[i][0] == '-')
00533         {
00534             if((OptionChar = argv[i][1]) == 0)
00535             {
00536                 ErrorMessage("Invalid parameter format.\n");
00537                 return 0;
00538             }
00539 
00540             if(argv[i][2])
00541                 OptionString = &argv[i][2];
00542             else if(++i < argc)
00543                 OptionString = argv[i];
00544             else
00545             {
00546                 ErrorMessage("Invalid parameter format.\n");
00547                 return 0;
00548             }
00549 
00550             switch(OptionChar)
00551             {
00552             case 'x':
00553                 Param->ScaleStr = OptionString;
00554                 break;
00555             case 'p':
00556                 Param->PsfSigma = (float)atof(OptionString);
00557 
00558                 if(Param->PsfSigma < 0.0)
00559                 {
00560                     ErrorMessage("Point spread blur size must be nonnegative.\n");
00561                     return 0;
00562                 }
00563                 break;
00564             case 'b':
00565                 if(!strcmp(OptionString, "const"))
00566                     Param->Boundary = BOUNDARY_CONSTANT;
00567                 else if(!strcmp(OptionString, "hsym"))
00568                     Param->Boundary = BOUNDARY_HSYMMETRIC;
00569                 else if(!strcmp(OptionString, "wsym"))
00570                     Param->Boundary = BOUNDARY_WSYMMETRIC;
00571                 else if(!strcmp(OptionString, "per"))
00572                     Param->Boundary = BOUNDARY_PERIODIC;
00573                 else
00574                 {
00575                     ErrorMessage("Boundary extension must be either \"const\", \"hsym\", or \"wsym\".\n");
00576                     return 0;
00577                 }
00578                 break;
00579             case 'g':
00580                 if(!strcmp(OptionString, "centered")
00581                     || !strcmp(OptionString, "center"))
00582                     Param->CenteredGrid = 1;
00583                 else if(!strcmp(OptionString, "topleft")
00584                     || !strcmp(OptionString, "top-left"))
00585                     Param->CenteredGrid = 0;
00586                 else
00587                 {
00588                     ErrorMessage("Grid must be either \"centered\" or \"topleft\".\n");
00589                     return 0;
00590                 }
00591                 break;
00592 
00593 #ifdef LIBJPEG_SUPPORT
00594             case 'q':
00595                 Param->JpegQuality = atoi(OptionString);
00596 
00597                 if(Param->JpegQuality <= 0 || Param->JpegQuality > 100)
00598                 {
00599                     ErrorMessage("JPEG quality must be between 0 and 100.\n");
00600                     return 0;
00601                 }
00602                 break;
00603 #endif
00604             case '-':
00605                 PrintHelpMessage();
00606                 return 0;
00607             default:
00608                 if(isprint(OptionChar))
00609                     ErrorMessage("Unknown option \"-%c\".\n", OptionChar);
00610                 else
00611                     ErrorMessage("Unknown option.\n");
00612 
00613                 return 0;
00614             }
00615 
00616             i++;
00617         }
00618         else
00619         {
00620             if(!Param->InputFile)
00621                 Param->InputFile = argv[i];
00622             else
00623                 Param->OutputFile = argv[i];
00624 
00625             i++;
00626         }
00627     }
00628 
00629     if(!Param->InputFile)
00630     {
00631         PrintHelpMessage();
00632         return 0;
00633     }
00634 
00635     return 1;
00636 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines