Linear Methods for Image Interpolation
linterp.c
Go to the documentation of this file.
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 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines