Linear Methods for Image Interpolation
|
00001 00023 #include <stdio.h> 00024 #include <stdlib.h> 00025 #include <string.h> 00026 #include <math.h> 00027 #include <fftw3.h> 00028 00029 #include "basic.h" 00030 #include "linterp.h" 00031 #include "lkernels.h" 00032 00034 #define CLAMP(X,A,B) (((X) < (A)) ? (A) : (((X) > (B)) ? (B) : (X))) 00035 00036 00043 static int ConstExtension(int N, int i) 00044 { 00045 if(i < 0) 00046 return 0; 00047 else if(i >= N) 00048 return N - 1; 00049 else 00050 return i; 00051 } 00052 00053 00060 static int HSymExtension(int N, int i) 00061 { 00062 while(1) 00063 { 00064 if(i < 0) 00065 i = -1 - i; 00066 else if(i >= N) 00067 i = (2*N - 1) - i; 00068 else 00069 return i; 00070 } 00071 } 00072 00073 00080 static int WSymExtension(int N, int i) 00081 { 00082 while(1) 00083 { 00084 if(i < 0) 00085 i = -i; 00086 else if(i >= N) 00087 i = (2*N - 2) - i; 00088 else 00089 return i; 00090 } 00091 } 00092 00093 00095 int (*ExtensionMethod[3])(int, int) = 00096 {ConstExtension, HSymExtension, WSymExtension}; 00097 00098 00146 int LinInterp2d(float *Dest, const float *Src, int SrcWidth, int SrcHeight, 00147 float *X, float *Y, int NumSamples, 00148 float (*Kernel)(float), float KernelRadius, int KernelNormalize, 00149 boundaryhandling Boundary) 00150 { 00151 int (*Extension)(int, int) = ExtensionMethod[Boundary]; 00152 const int KernelWidth = (int)ceil(2*KernelRadius); 00153 float *KernelXBuf = NULL, *KernelYBuf = NULL; 00154 float Weight, Sum, DenomSum; 00155 int IndexX0, IndexY0, IndexX, IndexY, SrcRowOffset; 00156 int k, m, n, Success = 0; 00157 00158 if(!Dest || !Src || SrcWidth <= 0 || SrcHeight <= 0 || !X || !Y 00159 || NumSamples < 0 || !Kernel || KernelRadius < 0 00160 || !(KernelXBuf = (float *)Malloc(sizeof(float)*(KernelWidth))) 00161 || !(KernelYBuf = (float *)Malloc(sizeof(float)*(KernelWidth)))) 00162 goto Catch; 00163 00164 if(!KernelNormalize) 00165 for(k = 0; k < NumSamples; k++) 00166 { 00167 IndexX0 = (int)ceil(X[k] - KernelRadius); 00168 IndexY0 = (int)ceil(Y[k] - KernelRadius); 00169 Sum = 0.0f; 00170 00171 /* Evaluate the kernel */ 00172 for(m = 0; m < KernelWidth; m++) 00173 KernelXBuf[m] = Kernel(X[k] - (IndexX0 + m)); 00174 00175 for(n = 0; n < KernelWidth; n++) 00176 KernelYBuf[n] = Kernel(Y[k] - (IndexY0 + n)); 00177 00178 /* Compute the interpolated value at (X[k], Y[k]) */ 00179 for(n = 0; n < KernelWidth; n++) 00180 { 00181 IndexY = Extension(SrcHeight, IndexY0 + n); 00182 SrcRowOffset = SrcWidth*IndexY; 00183 00184 for(m = 0; m < KernelWidth; m++) 00185 { 00186 IndexX = Extension(SrcWidth, IndexX0 + m); 00187 Sum += Src[IndexX + SrcRowOffset] 00188 * KernelXBuf[m] * KernelYBuf[n]; 00189 } 00190 } 00191 00192 Dest[k] = Sum; 00193 } 00194 else 00195 for(k = 0; k < NumSamples; k++) 00196 { 00197 IndexX0 = (int)ceil(X[k] - KernelRadius); 00198 IndexY0 = (int)ceil(Y[k] - KernelRadius); 00199 Sum = DenomSum = 0.0f; 00200 00201 /* Evaluate the kernel */ 00202 for(m = 0; m < KernelWidth; m++) 00203 KernelXBuf[m] = Kernel(X[k] - (IndexX0 + m)); 00204 00205 for(n = 0; n < KernelWidth; n++) 00206 KernelYBuf[n] = Kernel(Y[k] - (IndexY0 + n)); 00207 00208 /* Compute the interpolated value at (X[k], Y[k]) */ 00209 for(n = 0; n < KernelWidth; n++) 00210 { 00211 IndexY = Extension(SrcHeight, IndexY0 + n); 00212 SrcRowOffset = SrcWidth*IndexY; 00213 00214 for(m = 0; m < KernelWidth; m++) 00215 { 00216 IndexX = Extension(SrcWidth, IndexX0 + m); 00217 Weight = KernelXBuf[m] * KernelYBuf[n]; 00218 Sum += Src[IndexX + SrcRowOffset] * Weight; 00219 DenomSum += Weight; 00220 } 00221 } 00222 00223 Dest[k] = Sum / DenomSum; 00224 } 00225 00226 Success = 1; 00227 Catch: 00228 Free(KernelYBuf); 00229 Free(KernelXBuf); 00230 return Success; 00231 } 00232 00233 00252 int MakeScaleRotationGrid(float **X, float **Y, int *GridWidth, 00253 int *GridHeight, int SrcWidth, int SrcHeight, float Scale, float Theta) 00254 { 00255 const int ScaleWidth = floor(Scale*SrcWidth + 0.5f); 00256 const int ScaleHeight = floor(Scale*SrcHeight + 0.5f); 00257 const float x0 = (float)ScaleWidth/2.0f; 00258 const float y0 = (float)ScaleHeight/2.0f; 00259 const float CosTheta = (float)cos(Theta); 00260 const float SinTheta = (float)sin(Theta); 00261 float CurX, CurY, XStart, YStart, *XPtr, *YPtr; 00262 int m, n; 00263 00264 00265 if(!X || !Y || !GridWidth || !GridHeight 00266 || SrcWidth <= 0 || SrcHeight <= 0 || Scale <= 0) 00267 return 0; 00268 00269 *X = *Y = 0; 00270 00271 /* Determine the support of the transformed image. */ 00272 XStart = -fabs(CosTheta)*x0 - fabs(SinTheta)*y0; 00273 YStart = -fabs(SinTheta)*x0 - fabs(CosTheta)*y0; 00274 *GridWidth = floor(ScaleWidth*fabs(CosTheta) 00275 + ScaleHeight*fabs(SinTheta) + 0.5f); 00276 *GridHeight = floor(ScaleWidth*fabs(SinTheta) 00277 + ScaleHeight*fabs(CosTheta) + 0.5f); 00278 00279 if(!(*X = (float *)Malloc(sizeof(float)*(*GridWidth)*(*GridHeight))) 00280 || !(*Y = (float *)Malloc(sizeof(float)*(*GridWidth)*(*GridHeight)))) 00281 { 00282 *GridWidth = *GridHeight = 0; 00283 00284 Free(*X); 00285 return 0; 00286 } 00287 00288 XPtr = *X; 00289 YPtr = *Y; 00290 00291 /* Create the sampling grid */ 00292 for(n = 0; n < *GridHeight; n++) 00293 for(m = 0; m < *GridWidth; m++) 00294 { 00295 CurX = XStart + m; 00296 CurY = YStart + n; 00297 *(XPtr++) = (x0 + CosTheta*CurX - SinTheta*CurY) / Scale; 00298 *(YPtr++) = (y0 + SinTheta*CurX + CosTheta*CurY) / Scale; 00299 } 00300 00301 return 1; 00302 } 00303 00304 00305 typedef struct 00306 { 00307 float *Coeff; 00308 int16_t *Pos; 00309 int Width; 00310 } scalescanfilter; 00311 00312 00313 static void ScaleScan(float *Dest, int DestStride, int DestWidth, 00314 const float *Src, int SrcStride, 00315 scalescanfilter Filter) 00316 { 00317 float Sum; 00318 int x, k, SrcIndex; 00319 00320 00321 for(x = 0; x < DestWidth; x++) 00322 { 00323 SrcIndex = Filter.Pos[x] * SrcStride; 00324 Sum = 0; 00325 00326 for(k = 0; k < Filter.Width; k++, SrcIndex += SrcStride) 00327 Sum += Filter.Coeff[k] * Src[SrcIndex]; 00328 00329 *Dest = Sum; 00330 Dest += DestStride; 00331 Filter.Coeff += Filter.Width; 00332 } 00333 } 00334 00335 00355 static int MakeScaleScanFilter(scalescanfilter *Filter, 00356 int DestWidth, float XStart, float XStep, int SrcWidth, 00357 float (*Kernel)(float), float KernelRadius, int KernelNormalize, 00358 boundaryhandling Boundary) 00359 { 00360 int (*Extension)(int, int) = ExtensionMethod[Boundary]; 00361 const int KernelWidth = (int)ceil(2*KernelRadius); 00362 const int FilterWidth = (SrcWidth < KernelWidth) ? SrcWidth : KernelWidth; 00363 float *FilterCoeff = NULL; 00364 int16_t *FilterPos = NULL; 00365 float SrcX, Sum; 00366 int n, DestX, Pos, MaxPos; 00367 00368 00369 if(!(FilterCoeff = (float *)Malloc(sizeof(float)*FilterWidth*DestWidth)) 00370 || !(FilterPos = (int16_t *)Malloc(sizeof(int16_t)*DestWidth))) 00371 { 00372 Free(FilterCoeff); 00373 Filter->Coeff = NULL; 00374 Filter->Pos = 0; 00375 Filter->Width = 0; 00376 return 0; 00377 } 00378 00379 Filter->Coeff = FilterCoeff; 00380 Filter->Pos = FilterPos; 00381 Filter->Width = FilterWidth; 00382 MaxPos = SrcWidth - FilterWidth; 00383 00384 for(DestX = 0; DestX < DestWidth; DestX++) 00385 { 00386 SrcX = XStart + XStep*DestX; 00387 Pos = (int)ceil(SrcX - KernelRadius); 00388 00389 if(Pos < 0 || MaxPos < Pos) 00390 { 00391 FilterPos[DestX] = CLAMP(Pos, 0, MaxPos); 00392 00393 for(n = 0; n < FilterWidth; n++) 00394 FilterCoeff[n] = 0; 00395 00396 for(n = 0; n < KernelWidth; n++) 00397 FilterCoeff[Extension(SrcWidth, Pos + n) - FilterPos[DestX]] 00398 += Kernel(SrcX - (Pos + n)); 00399 } 00400 else 00401 { 00402 FilterPos[DestX] = Pos; 00403 00404 for(n = 0; n < FilterWidth; n++) 00405 FilterCoeff[n] = Kernel(SrcX - (Pos + n)); 00406 } 00407 00408 if(KernelNormalize) /* Normalize */ 00409 { 00410 Sum = 0; 00411 00412 for(n = 0; n < FilterWidth; n++) 00413 Sum += FilterCoeff[n]; 00414 00415 for(n = 0; n < FilterWidth; n++) 00416 FilterCoeff[n] /= Sum; 00417 } 00418 00419 FilterCoeff += FilterWidth; 00420 } 00421 00422 return 1; 00423 } 00424 00425 00463 int LinScale2d(float *Dest, int DestWidth, float XStart, float XStep, 00464 int DestHeight, float YStart, float YStep, 00465 const float *Src, int SrcWidth, int SrcHeight, int NumChannels, 00466 float (*Kernel)(float), float KernelRadius, int KernelNormalize, 00467 boundaryhandling Boundary) 00468 { 00469 const int SrcNumPixels = SrcWidth*SrcHeight; 00470 const int DestNumPixels = DestWidth*DestHeight; 00471 scalescanfilter HFilter = {NULL, 0, 0}, VFilter = {NULL, 0, 0}; 00472 float *Buf = NULL; 00473 int x, y, Channel, Success = 0; 00474 00475 00476 if(!Dest || DestWidth <= 0 || DestHeight <= 0 || !Src 00477 || SrcWidth <= 0 || SrcHeight <= 0 || NumChannels <= 0 00478 || !Kernel || KernelRadius < 0) 00479 return 0; 00480 if(!(Buf = (float *)Malloc(sizeof(float)*SrcWidth*DestHeight)) 00481 || !MakeScaleScanFilter(&HFilter, DestWidth, XStart, XStep, 00482 SrcWidth, Kernel, KernelRadius, KernelNormalize, Boundary) 00483 || !MakeScaleScanFilter(&VFilter, DestHeight, YStart, YStep, 00484 SrcHeight, Kernel, KernelRadius, KernelNormalize, Boundary)) 00485 goto Catch; 00486 00487 for(Channel = 0; Channel < NumChannels; Channel++) 00488 { 00489 for(x = 0; x < SrcWidth; x++) 00490 ScaleScan(Buf + x, SrcWidth, DestHeight, 00491 Src + x, SrcWidth, VFilter); 00492 00493 for(y = 0; y < DestHeight; y++) 00494 ScaleScan(Dest + y*DestWidth, 1, DestWidth, 00495 Buf + y*SrcWidth, 1, HFilter); 00496 00497 Src += SrcNumPixels; 00498 Dest += DestNumPixels; 00499 } 00500 00501 Success = 1; 00502 Catch: 00503 Free(VFilter.Pos); 00504 Free(VFilter.Coeff); 00505 Free(HFilter.Pos); 00506 Free(HFilter.Coeff); 00507 Free(Buf); 00508 return Success; 00509 } 00510 00511 00512 static int FourierScaleScan(float *Dest, 00513 int DestStride, int DestScanStride, int DestChannelStride, int DestScanSize, 00514 const float *Src, 00515 int SrcStride, int SrcScanStride, int SrcChannelStride, int SrcScanSize, 00516 int NumScans, int NumChannels, float XStart, double PsfSigma, 00517 boundaryhandling Boundary) 00518 { 00519 const int SrcPadScanSize = 2*(SrcScanSize 00520 - ((Boundary == BOUNDARY_WSYMMETRIC) ? 1:0)); 00521 const int DestPadScanSize = (Boundary == BOUNDARY_HSYMMETRIC) ? 00522 (2*DestScanSize) 00523 : (2*DestScanSize - floor((2.0f*DestScanSize)/SrcScanSize + 0.5f)); 00524 const int ReflectOffset = SrcPadScanSize 00525 - ((Boundary == BOUNDARY_HSYMMETRIC) ? 1:0); 00526 const int SrcDftSize = SrcPadScanSize/2 + 1; 00527 const int DestDftSize = DestPadScanSize/2 + 1; 00528 const int BufSpatialNumEl = DestPadScanSize*NumScans*NumChannels; 00529 const int BufDftNumEl = 2*DestDftSize*NumScans*NumChannels; 00530 float *BufSpatial = NULL, *BufDft = NULL, *Modulation = NULL, *Ptr; 00531 fftwf_plan Plan = 0; 00532 fftwf_iodim Dims[1], HowManyDims[1]; 00533 float Temp, Denom; 00534 int i, Scan, Channel, Success = 0; 00535 00536 00537 if((Boundary != BOUNDARY_HSYMMETRIC && Boundary != BOUNDARY_WSYMMETRIC) 00538 || !(BufSpatial = (float *)fftwf_malloc(sizeof(float)*BufSpatialNumEl)) 00539 || !(BufDft = (float *)fftwf_malloc(sizeof(float)*BufDftNumEl))) 00540 goto Catch; 00541 00542 if(XStart != 0) 00543 { 00544 if(!(Modulation = (float *)Malloc(sizeof(float)*2*DestDftSize))) 00545 goto Catch; 00546 00547 for(i = 0; i < DestDftSize; i++) 00548 { 00549 Temp = M_2PI*XStart*i/SrcPadScanSize; 00550 Modulation[2*i + 0] = cos(Temp); 00551 Modulation[2*i + 1] = sin(Temp); 00552 } 00553 } 00554 00555 /* Fill BufSpatial with the input and symmetrize */ 00556 for(Channel = 0; Channel < NumChannels; Channel++) 00557 { 00558 for(Scan = 0; Scan < NumScans; Scan++) 00559 { 00560 for(i = 0; i < SrcScanSize; i++) 00561 BufSpatial[i + SrcPadScanSize*(Scan + NumScans*Channel)] 00562 = Src[SrcStride*i + SrcScanStride*Scan 00563 + SrcChannelStride*Channel]; 00564 00565 for(; i < SrcPadScanSize; i++) 00566 BufSpatial[i + SrcPadScanSize*(Scan + NumScans*Channel)] 00567 = Src[SrcStride*(ReflectOffset - i) 00568 + SrcScanStride*Scan + SrcChannelStride*Channel]; 00569 } 00570 } 00571 00572 /* Initialize DFT buffer to zeros (there is no "fftwf_calloc"). Note that 00573 it is not safely portable to use memset for this purpose. 00574 http://c-faq.com/malloc/calloc.html */ 00575 for(i = 0; i < BufDftNumEl; i++) 00576 BufDft[i] = 0.0f; 00577 00578 /* Perform DFT real-to-complex transform */ 00579 Dims[0].n = SrcPadScanSize; 00580 Dims[0].is = 1; 00581 Dims[0].os = 1; 00582 HowManyDims[0].n = NumScans*NumChannels; 00583 HowManyDims[0].is = SrcPadScanSize; 00584 HowManyDims[0].os = DestDftSize; 00585 00586 if(!(Plan = fftwf_plan_guru_dft_r2c(1, Dims, 1, HowManyDims, BufSpatial, 00587 (fftwf_complex *)BufDft, FFTW_ESTIMATE | FFTW_DESTROY_INPUT))) 00588 goto Catch; 00589 00590 fftwf_execute(Plan); 00591 fftwf_destroy_plan(Plan); 00592 00593 if(PsfSigma == 0) 00594 for(Channel = 0, Ptr = BufDft; Channel < NumChannels; Channel++) 00595 for(Scan = 0; Scan < NumScans; Scan++, Ptr += 2*DestDftSize) 00596 for(i = 0; i < SrcDftSize; i++) 00597 { 00598 Ptr[2*i + 0] /= SrcPadScanSize; 00599 Ptr[2*i + 1] /= SrcPadScanSize; 00600 } 00601 else 00602 { 00603 /* Also divide by the Gaussian point spread function in this case */ 00604 Temp = SrcPadScanSize / (M_2PI * PsfSigma); 00605 Temp = 2*Temp*Temp; 00606 00607 for(i = 0; i < SrcDftSize; i++) 00608 { 00609 if(i <= DestScanSize) 00610 Denom = exp(-(i*i)/Temp); 00611 else 00612 Denom = exp(-((DestPadScanSize - i)*(DestPadScanSize - i))/Temp); 00613 00614 Denom *= SrcPadScanSize; 00615 00616 for(Channel = 0; Channel < NumChannels; Channel++) 00617 for(Scan = 0; Scan < NumScans; Scan++) 00618 { 00619 BufDft[2*(i + DestDftSize*(Scan + NumScans*Channel)) + 0] 00620 /= Denom; 00621 BufDft[2*(i + DestDftSize*(Scan + NumScans*Channel)) + 1] 00622 /= Denom; 00623 } 00624 } 00625 } 00626 00627 /* If XStart is nonzero, modulate the DFT to translate the result */ 00628 if(XStart != 0) 00629 for(Channel = 0, Ptr = BufDft; Channel < NumChannels; Channel++) 00630 for(Scan = 0; Scan < NumScans; Scan++, Ptr += 2*DestDftSize) 00631 for(i = 0; i < SrcDftSize; i++) 00632 { 00633 /* Complex multiply */ 00634 Temp = Ptr[2*i + 0]*Modulation[2*i + 0] 00635 - Ptr[2*i + 1]*Modulation[2*i + 1]; 00636 Ptr[2*i + 1] = Ptr[2*i + 0]*Modulation[2*i + 1] 00637 + Ptr[2*i + 1]*Modulation[2*i + 0]; 00638 Ptr[2*i + 0] = Temp; 00639 } 00640 00641 /* Perform inverse DFT complex-to-real transform */ 00642 Dims[0].n = DestPadScanSize; 00643 Dims[0].is = 1; 00644 Dims[0].os = 1; 00645 HowManyDims[0].n = NumScans*NumChannels; 00646 HowManyDims[0].is = DestDftSize; 00647 HowManyDims[0].os = DestPadScanSize; 00648 00649 if(!(Plan = fftwf_plan_guru_dft_c2r(1, Dims, 1, HowManyDims, 00650 (fftwf_complex *)BufDft, BufSpatial, 00651 FFTW_ESTIMATE | FFTW_DESTROY_INPUT))) 00652 goto Catch; 00653 00654 fftwf_execute(Plan); 00655 fftwf_destroy_plan(Plan); 00656 00657 /* Fill Dest with the result (and trim padding) */ 00658 for(Channel = 0; Channel < NumChannels; Channel++) 00659 { 00660 for(Scan = 0; Scan < NumScans; Scan++) 00661 { 00662 for(i = 0; i < DestScanSize; i++) 00663 Dest[DestStride*i + DestScanStride*Scan 00664 + DestChannelStride*Channel] 00665 = BufSpatial[i + DestPadScanSize*(Scan + NumScans*Channel)]; 00666 } 00667 } 00668 00669 Success = 1; 00670 Catch: 00671 Free(Modulation); 00672 if(BufDft) 00673 fftwf_free(BufDft); 00674 if(BufSpatial) 00675 fftwf_free(BufSpatial); 00676 fftwf_cleanup(); 00677 return Success; 00678 } 00679 00680 00705 int FourierScale2d(float *Dest, int DestWidth, float XStart, 00706 int DestHeight, float YStart, 00707 const float *Src, int SrcWidth, int SrcHeight, int NumChannels, 00708 double PsfSigma, boundaryhandling Boundary) 00709 { 00710 float *Buf = NULL; 00711 int Success = 0; 00712 00713 unsigned long StartTime, StopTime; 00714 00715 00716 if(!Dest || DestWidth < SrcWidth || DestHeight < SrcHeight || !Src 00717 || SrcWidth <= 0 || SrcHeight <= 0 || NumChannels <= 0 || PsfSigma < 0 00718 || !(Buf = (float *)Malloc(sizeof(float)*SrcWidth*DestHeight*3))) 00719 return 0; 00720 00721 StartTime = Clock(); 00722 00723 /* Scale the image vertically */ 00724 if(!FourierScaleScan(Buf, SrcWidth, 1, SrcWidth*DestHeight, DestHeight, 00725 Src, SrcWidth, 1, SrcWidth*SrcHeight, SrcHeight, 00726 SrcWidth, 3, YStart, PsfSigma, Boundary)) 00727 goto Catch; 00728 00729 /* Scale the image horizontally */ 00730 if(!FourierScaleScan(Dest, 1, DestWidth, DestWidth*DestHeight, DestWidth, 00731 Buf, 1, SrcWidth, SrcWidth*DestHeight, SrcWidth, 00732 DestHeight, 3, XStart, PsfSigma, Boundary)) 00733 goto Catch; 00734 00735 StopTime = Clock(); 00736 printf("CPU Time: %.3f s\n\n", 0.001*(StopTime - StartTime)); 00737 00738 Success = 1; 00739 Catch: 00740 Free(Buf); 00741 return Success; 00742 }