Image Interpolation with Contour Stencils
|
00001 00020 #include <math.h> 00021 #include <string.h> 00022 #include <ctype.h> 00023 00024 #include "imageio.h" 00025 00026 #define VERBOSE 0 00027 00029 #define NUMSTDS 4 00030 00031 00033 typedef struct 00034 { 00036 uint32_t *Data; 00038 int Width; 00040 int Height; 00041 } image; 00042 00043 typedef enum 00044 { 00045 BOUNDARY_CONSTANT = 0, 00046 BOUNDARY_HSYMMETRIC = 1, 00047 BOUNDARY_WSYMMETRIC = 2 00048 } boundaryhandling; 00049 00051 typedef struct 00052 { 00054 char *InputFile; 00056 char *OutputFile; 00058 int JpegQuality; 00060 int CenteredGrid; 00062 boundaryhandling Boundary; 00064 float ScaleFactor; 00066 float PsfSigma; 00067 } programparams; 00068 00069 00070 int ParseParams(programparams *Param, int argc, char *argv[]); 00071 int Coarsen(image v, image u, programparams Param); 00072 00074 void PrintHelpMessage() 00075 { 00076 printf("Image coarsening utility, P. Getreuer 2010-2011\n\n"); 00077 printf("Usage: imcoarsen [options] <input file> <output file>\n\n" 00078 "Only " READIMAGE_FORMATS_SUPPORTED " images are supported.\n\n"); 00079 printf("Options:\n"); 00080 printf(" -x <number> the coarsening factor (>= 1.0, may be non-integer)\n"); 00081 printf(" -p <number> sigma_h, the blur size of the Gaussian point spread function\n" 00082 " in units of output pixels.\n"); 00083 printf(" -b <ext> extension to use for boundary handling, choices for <ext> are\n"); 00084 printf(" const constant extension\n"); 00085 printf(" hsym half-sample symmetric (default)\n"); 00086 printf(" wsym whole-sample symmetric\n"); 00087 printf(" -g <grid> grid to use for resampling, choices for <grid> are\n" 00088 " centered grid with centered alignment (default)\n" 00089 " topleft the top-left anchored grid\n\n"); 00090 #ifdef LIBJPEG_SUPPORT 00091 printf(" -q <number> quality for saving JPEG images (0 to 100)\n\n"); 00092 #endif 00093 printf("Example: coarsen by factor 2.5\n" 00094 " imcoarsen -x 2.5 -p 0.35 frog.bmp coarse.bmp\n"); 00095 } 00096 00097 00098 int main(int argc, char *argv[]) 00099 { 00100 programparams Param; 00101 image u = {NULL, 0, 0}, v = {NULL, 0, 0}; 00102 int Status = 1; 00103 00104 00105 if(!ParseParams(&Param, argc, argv)) 00106 return 0; 00107 00108 /* Read the input image */ 00109 if(!(u.Data = (uint32_t *)ReadImage(&u.Width, &u.Height, Param.InputFile, 00110 IMAGEIO_U8 | IMAGEIO_RGBA))) 00111 goto Catch; 00112 00113 if(Param.ScaleFactor >= u.Width || Param.ScaleFactor >= u.Height) 00114 { 00115 ErrorMessage("Image is too small for scale factor.\n"); 00116 goto Catch; 00117 } 00118 00119 /* Allocate the output image */ 00120 v.Width = (int)ceil(u.Width / Param.ScaleFactor); 00121 v.Height = (int)ceil(u.Height / Param.ScaleFactor); 00122 #if VERBOSE > 0 00123 printf("%dx%d input --> %dx%d output\n", u.Width, u.Height, v.Width, v.Height); 00124 #endif 00125 00126 if(!(v.Data = (uint32_t *)Malloc(sizeof(uint32_t)* 00127 ((long int)v.Width)*((long int)v.Height)))) 00128 goto Catch; 00129 00130 /* Convolution followed by downsampling */ 00131 if(!Coarsen(v, u, Param)) 00132 goto Catch; 00133 00134 /* Write the output image */ 00135 if(!WriteImage(v.Data, v.Width, v.Height, Param.OutputFile, 00136 IMAGEIO_U8 | IMAGEIO_RGBA, Param.JpegQuality)) 00137 goto Catch; 00138 #if VERBOSE > 0 00139 else 00140 printf("Output written to \"%s\".\n", Param.OutputFile); 00141 #endif 00142 00143 Status = 0; /* Finished successfully, set exit status to zero. */ 00144 00145 Catch: 00146 Free(v.Data); 00147 Free(u.Data); 00148 return Status; 00149 } 00150 00151 00152 float Sqr(float x) 00153 { 00154 return x*x; 00155 } 00156 00163 static int ConstExtension(int N, int i) 00164 { 00165 if(i < 0) 00166 return 0; 00167 else if(i >= N) 00168 return N - 1; 00169 else 00170 return i; 00171 } 00172 00173 00180 static int HSymExtension(int N, int i) 00181 { 00182 while(1) 00183 { 00184 if(i < 0) 00185 i = -1 - i; 00186 else if(i >= N) 00187 i = (2*N - 1) - i; 00188 else 00189 return i; 00190 } 00191 } 00192 00193 00200 static int WSymExtension(int N, int i) 00201 { 00202 while(1) 00203 { 00204 if(i < 0) 00205 i = -i; 00206 else if(i >= N) 00207 i = (2*N - 2) - i; 00208 else 00209 return i; 00210 } 00211 } 00212 00213 00214 int (*ExtensionMethod[3])(int, int) = 00215 {ConstExtension, HSymExtension, WSymExtension}; 00216 00217 00218 int Coarsen(image v, image u, programparams Param) 00219 { 00220 int (*Extension)(int, int) = ExtensionMethod[Param.Boundary]; 00221 const float PsfRadius = NUMSTDS*Param.PsfSigma*Param.ScaleFactor; 00222 const int PsfWidth = (PsfRadius == 0) ? 1 : (int)ceil(2*PsfRadius); 00223 float *Temp = NULL, *PsfBuf = NULL; 00224 float ExpDenom, Weight, Sum[4], DenomSum; 00225 float XStart, YStart, X, Y; 00226 uint32_t Pixel; 00227 int IndexX0, IndexY0, SrcOffset, DestOffset; 00228 int x, y, n, c, Success = 0; 00229 00230 00231 if(!(Temp = (float *)Malloc(sizeof(float)*4*v.Width*u.Height)) 00232 || !(PsfBuf = (float *)Malloc(sizeof(float)*PsfWidth))) 00233 goto Catch; 00234 00235 if(Param.CenteredGrid) 00236 { 00237 XStart = (1/Param.ScaleFactor - 1)/2; 00238 YStart = (1/Param.ScaleFactor - 1)/2; 00239 } 00240 else 00241 XStart = YStart = 0; 00242 00243 ExpDenom = 2 * Sqr(Param.PsfSigma*Param.ScaleFactor); 00244 00245 for(x = 0; x < v.Width; x++) 00246 { 00247 X = (-XStart + x)*Param.ScaleFactor; 00248 IndexX0 = (int)floor(X - PsfRadius + 0.5f); 00249 DenomSum = 0; 00250 00251 /* Evaluate the PSF */ 00252 for(n = 0; n < PsfWidth; n++) 00253 { 00254 PsfBuf[n] = Sqr(X - (IndexX0 + n)); 00255 00256 if(!n || PsfBuf[n] < DenomSum) 00257 DenomSum = PsfBuf[n]; 00258 } 00259 00260 if(ExpDenom > 0) 00261 for(n = 0; n < PsfWidth; n++) 00262 PsfBuf[n] = (float)exp((DenomSum - PsfBuf[n]) / ExpDenom); 00263 else 00264 PsfBuf[0] = 1; 00265 00266 DenomSum = 0; 00267 00268 for(n = 0; n < PsfWidth; n++) 00269 DenomSum += PsfBuf[n]; 00270 00271 for(y = 0, SrcOffset = 0, DestOffset = x; y < u.Height; 00272 y++, SrcOffset += u.Width, DestOffset += v.Width) 00273 { 00274 Sum[0] = Sum[1] = Sum[2] = Sum[3] = 0; 00275 00276 for(n = 0; n < PsfWidth; n++) 00277 { 00278 Weight = PsfBuf[n]; 00279 Pixel = u.Data[Extension(u.Width, IndexX0 + n) + SrcOffset]; 00280 00281 for(c = 0; c < 4; c++) 00282 Sum[c] += (float)((uint8_t *)&Pixel)[c] * Weight; 00283 } 00284 00285 for(c = 0; c < 4; c++) 00286 Temp[4*DestOffset + c] = Sum[c] / DenomSum; 00287 } 00288 } 00289 00290 for(y = 0; y < v.Height; y++, v.Data += v.Width) 00291 { 00292 Y = (-YStart + y)*Param.ScaleFactor; 00293 IndexY0 = (int)floor(Y - PsfRadius + 0.5f); 00294 DenomSum = 0; 00295 00296 /* Evaluate the PSF */ 00297 for(n = 0; n < PsfWidth; n++) 00298 { 00299 PsfBuf[n] = Sqr(Y - (IndexY0 + n)); 00300 00301 if(!n || PsfBuf[n] < DenomSum) 00302 DenomSum = PsfBuf[n]; 00303 } 00304 00305 if(ExpDenom > 0) 00306 for(n = 0; n < PsfWidth; n++) 00307 PsfBuf[n] = (float)exp((DenomSum - PsfBuf[n]) / ExpDenom); 00308 else 00309 PsfBuf[0] = 1; 00310 00311 DenomSum = 0; 00312 00313 for(n = 0; n < PsfWidth; n++) 00314 DenomSum += PsfBuf[n]; 00315 00316 for(x = 0; x < v.Width; x++) 00317 { 00318 Sum[0] = Sum[1] = Sum[2] = Sum[3] = 0; 00319 00320 for(n = 0; n < PsfWidth; n++) 00321 { 00322 SrcOffset = x + v.Width*Extension(u.Height, IndexY0 + n); 00323 Weight = PsfBuf[n]; 00324 00325 for(c = 0; c < 4; c++) 00326 Sum[c] += Temp[4*SrcOffset + c] * Weight; 00327 } 00328 00329 for(c = 0; c < 4; c++) 00330 ((uint8_t *)&Pixel)[c] = (int)(Sum[c] / DenomSum + 0.5f); 00331 00332 v.Data[x] = Pixel; 00333 } 00334 } 00335 00336 Success = 1; 00337 Catch: 00338 Free(PsfBuf); 00339 Free(Temp); 00340 return Success; 00341 } 00342 00343 00344 int ParseParams(programparams *Param, int argc, char *argv[]) 00345 { 00346 static char *DefaultOutputFile = (char *)"out.bmp"; 00347 char *OptionString; 00348 char OptionChar; 00349 int i; 00350 00351 00352 if(argc < 2) 00353 { 00354 PrintHelpMessage(); 00355 return 0; 00356 } 00357 00358 /* Set parameter defaults */ 00359 Param->InputFile = 0; 00360 Param->OutputFile = DefaultOutputFile; 00361 Param->JpegQuality = 99; 00362 Param->ScaleFactor = 1; 00363 Param->PsfSigma = 0.35f; 00364 Param->CenteredGrid = 1; 00365 Param->Boundary = BOUNDARY_HSYMMETRIC; 00366 00367 for(i = 1; i < argc;) 00368 { 00369 if(argv[i] && argv[i][0] == '-') 00370 { 00371 if((OptionChar = argv[i][1]) == 0) 00372 { 00373 ErrorMessage("Invalid parameter format.\n"); 00374 return 0; 00375 } 00376 00377 if(argv[i][2]) 00378 OptionString = &argv[i][2]; 00379 else if(++i < argc) 00380 OptionString = argv[i]; 00381 else 00382 { 00383 ErrorMessage("Invalid parameter format.\n"); 00384 return 0; 00385 } 00386 00387 switch(OptionChar) 00388 { 00389 case 'x': 00390 Param->ScaleFactor = (float)atof(OptionString); 00391 00392 if(Param->ScaleFactor < 1) 00393 { 00394 ErrorMessage("Invalid scale factor.\n"); 00395 return 0; 00396 } 00397 break; 00398 case 'p': 00399 Param->PsfSigma = (float)atof(OptionString); 00400 00401 if(Param->PsfSigma < 0.0) 00402 { 00403 ErrorMessage("Point spread blur size must be nonnegative.\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 ErrorMessage("Boundary extension must be either \"const\", \"hsym\", or \"wsym\".\n"); 00417 return 0; 00418 } 00419 break; 00420 case 'g': 00421 if(!strcmp(OptionString, "centered") 00422 || !strcmp(OptionString, "center")) 00423 Param->CenteredGrid = 1; 00424 else if(!strcmp(OptionString, "topleft") 00425 || !strcmp(OptionString, "top-left")) 00426 Param->CenteredGrid = 0; 00427 else 00428 { 00429 ErrorMessage("Grid must be either \"centered\" or \"topleft\".\n"); 00430 return 0; 00431 } 00432 break; 00433 00434 #ifdef LIBJPEG_SUPPORT 00435 case 'q': 00436 Param->JpegQuality = atoi(OptionString); 00437 00438 if(Param->JpegQuality <= 0 || Param->JpegQuality > 100) 00439 { 00440 ErrorMessage("JPEG quality must be between 0 and 100.\n"); 00441 return 0; 00442 } 00443 break; 00444 #endif 00445 case '-': 00446 PrintHelpMessage(); 00447 return 0; 00448 default: 00449 if(isprint(OptionChar)) 00450 ErrorMessage("Unknown option \"-%c\".\n", OptionChar); 00451 else 00452 ErrorMessage("Unknown option.\n"); 00453 00454 return 0; 00455 } 00456 00457 i++; 00458 } 00459 else 00460 { 00461 if(!Param->InputFile) 00462 Param->InputFile = argv[i]; 00463 else 00464 Param->OutputFile = argv[i]; 00465 00466 i++; 00467 } 00468 } 00469 00470 if(!Param->InputFile) 00471 { 00472 PrintHelpMessage(); 00473 return 0; 00474 } 00475 00476 return 1; 00477 }