Linear Methods for Image Interpolation
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros
linterp.c
Go to the documentation of this file.
1 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <math.h>
27 #include <fftw3.h>
28 
29 #include "basic.h"
30 #include "linterp.h"
31 #include "lkernels.h"
32 
34 #define CLAMP(X,A,B) (((X) < (A)) ? (A) : (((X) > (B)) ? (B) : (X)))
35 
36 
43 static int ConstExtension(int N, int i)
44 {
45  if(i < 0)
46  return 0;
47  else if(i >= N)
48  return N - 1;
49  else
50  return i;
51 }
52 
53 
60 static int HSymExtension(int N, int i)
61 {
62  while(1)
63  {
64  if(i < 0)
65  i = -1 - i;
66  else if(i >= N)
67  i = (2*N - 1) - i;
68  else
69  return i;
70  }
71 }
72 
73 
80 static int WSymExtension(int N, int i)
81 {
82  while(1)
83  {
84  if(i < 0)
85  i = -i;
86  else if(i >= N)
87  i = (2*N - 2) - i;
88  else
89  return i;
90  }
91 }
92 
93 
95 int (*ExtensionMethod[3])(int, int) =
96  {ConstExtension, HSymExtension, WSymExtension};
97 
98 
146 int LinInterp2d(float *Dest, const float *Src, int SrcWidth, int SrcHeight,
147  float *X, float *Y, int NumSamples,
148  float (*Kernel)(float), float KernelRadius, int KernelNormalize,
149  boundaryhandling Boundary)
150 {
151  int (*Extension)(int, int) = ExtensionMethod[Boundary];
152  const int KernelWidth = (int)ceil(2*KernelRadius);
153  float *KernelXBuf = NULL, *KernelYBuf = NULL;
154  float Weight, Sum, DenomSum;
155  int IndexX0, IndexY0, IndexX, IndexY, SrcRowOffset;
156  int k, m, n, Success = 0;
157 
158  if(!Dest || !Src || SrcWidth <= 0 || SrcHeight <= 0 || !X || !Y
159  || NumSamples < 0 || !Kernel || KernelRadius < 0
160  || !(KernelXBuf = (float *)Malloc(sizeof(float)*(KernelWidth)))
161  || !(KernelYBuf = (float *)Malloc(sizeof(float)*(KernelWidth))))
162  goto Catch;
163 
164  if(!KernelNormalize)
165  for(k = 0; k < NumSamples; k++)
166  {
167  IndexX0 = (int)ceil(X[k] - KernelRadius);
168  IndexY0 = (int)ceil(Y[k] - KernelRadius);
169  Sum = 0.0f;
170 
171  /* Evaluate the kernel */
172  for(m = 0; m < KernelWidth; m++)
173  KernelXBuf[m] = Kernel(X[k] - (IndexX0 + m));
174 
175  for(n = 0; n < KernelWidth; n++)
176  KernelYBuf[n] = Kernel(Y[k] - (IndexY0 + n));
177 
178  /* Compute the interpolated value at (X[k], Y[k]) */
179  for(n = 0; n < KernelWidth; n++)
180  {
181  IndexY = Extension(SrcHeight, IndexY0 + n);
182  SrcRowOffset = SrcWidth*IndexY;
183 
184  for(m = 0; m < KernelWidth; m++)
185  {
186  IndexX = Extension(SrcWidth, IndexX0 + m);
187  Sum += Src[IndexX + SrcRowOffset]
188  * KernelXBuf[m] * KernelYBuf[n];
189  }
190  }
191 
192  Dest[k] = Sum;
193  }
194  else
195  for(k = 0; k < NumSamples; k++)
196  {
197  IndexX0 = (int)ceil(X[k] - KernelRadius);
198  IndexY0 = (int)ceil(Y[k] - KernelRadius);
199  Sum = DenomSum = 0.0f;
200 
201  /* Evaluate the kernel */
202  for(m = 0; m < KernelWidth; m++)
203  KernelXBuf[m] = Kernel(X[k] - (IndexX0 + m));
204 
205  for(n = 0; n < KernelWidth; n++)
206  KernelYBuf[n] = Kernel(Y[k] - (IndexY0 + n));
207 
208  /* Compute the interpolated value at (X[k], Y[k]) */
209  for(n = 0; n < KernelWidth; n++)
210  {
211  IndexY = Extension(SrcHeight, IndexY0 + n);
212  SrcRowOffset = SrcWidth*IndexY;
213 
214  for(m = 0; m < KernelWidth; m++)
215  {
216  IndexX = Extension(SrcWidth, IndexX0 + m);
217  Weight = KernelXBuf[m] * KernelYBuf[n];
218  Sum += Src[IndexX + SrcRowOffset] * Weight;
219  DenomSum += Weight;
220  }
221  }
222 
223  Dest[k] = Sum / DenomSum;
224  }
225 
226  Success = 1;
227 Catch:
228  Free(KernelYBuf);
229  Free(KernelXBuf);
230  return Success;
231 }
232 
233 
252 int MakeScaleRotationGrid(float **X, float **Y, int *GridWidth,
253  int *GridHeight, int SrcWidth, int SrcHeight, float Scale, float Theta)
254 {
255  const int ScaleWidth = floor(Scale*SrcWidth + 0.5f);
256  const int ScaleHeight = floor(Scale*SrcHeight + 0.5f);
257  const float x0 = (float)ScaleWidth/2.0f;
258  const float y0 = (float)ScaleHeight/2.0f;
259  const float CosTheta = (float)cos(Theta);
260  const float SinTheta = (float)sin(Theta);
261  float CurX, CurY, XStart, YStart, *XPtr, *YPtr;
262  int m, n;
263 
264 
265  if(!X || !Y || !GridWidth || !GridHeight
266  || SrcWidth <= 0 || SrcHeight <= 0 || Scale <= 0)
267  return 0;
268 
269  *X = *Y = 0;
270 
271  /* Determine the support of the transformed image. */
272  XStart = -fabs(CosTheta)*x0 - fabs(SinTheta)*y0;
273  YStart = -fabs(SinTheta)*x0 - fabs(CosTheta)*y0;
274  *GridWidth = floor(ScaleWidth*fabs(CosTheta)
275  + ScaleHeight*fabs(SinTheta) + 0.5f);
276  *GridHeight = floor(ScaleWidth*fabs(SinTheta)
277  + ScaleHeight*fabs(CosTheta) + 0.5f);
278 
279  if(!(*X = (float *)Malloc(sizeof(float)*(*GridWidth)*(*GridHeight)))
280  || !(*Y = (float *)Malloc(sizeof(float)*(*GridWidth)*(*GridHeight))))
281  {
282  *GridWidth = *GridHeight = 0;
283 
284  Free(*X);
285  return 0;
286  }
287 
288  XPtr = *X;
289  YPtr = *Y;
290 
291  /* Create the sampling grid */
292  for(n = 0; n < *GridHeight; n++)
293  for(m = 0; m < *GridWidth; m++)
294  {
295  CurX = XStart + m;
296  CurY = YStart + n;
297  *(XPtr++) = (x0 + CosTheta*CurX - SinTheta*CurY) / Scale;
298  *(YPtr++) = (y0 + SinTheta*CurX + CosTheta*CurY) / Scale;
299  }
300 
301  return 1;
302 }
303 
304 
305 typedef struct
306 {
307  float *Coeff;
308  int16_t *Pos;
309  int Width;
311 
312 
313 static void ScaleScan(float *Dest, int DestStride, int DestWidth,
314  const float *Src, int SrcStride,
315  scalescanfilter Filter)
316 {
317  float Sum;
318  int x, k, SrcIndex;
319 
320 
321  for(x = 0; x < DestWidth; x++)
322  {
323  SrcIndex = Filter.Pos[x] * SrcStride;
324  Sum = 0;
325 
326  for(k = 0; k < Filter.Width; k++, SrcIndex += SrcStride)
327  Sum += Filter.Coeff[k] * Src[SrcIndex];
328 
329  *Dest = Sum;
330  Dest += DestStride;
331  Filter.Coeff += Filter.Width;
332  }
333 }
334 
335 
355 static int MakeScaleScanFilter(scalescanfilter *Filter,
356  int DestWidth, float XStart, float XStep, int SrcWidth,
357  float (*Kernel)(float), float KernelRadius, int KernelNormalize,
358  boundaryhandling Boundary)
359 {
360  int (*Extension)(int, int) = ExtensionMethod[Boundary];
361  const int KernelWidth = (int)ceil(2*KernelRadius);
362  const int FilterWidth = (SrcWidth < KernelWidth) ? SrcWidth : KernelWidth;
363  float *FilterCoeff = NULL;
364  int16_t *FilterPos = NULL;
365  float SrcX, Sum;
366  int n, DestX, Pos, MaxPos;
367 
368 
369  if(!(FilterCoeff = (float *)Malloc(sizeof(float)*FilterWidth*DestWidth))
370  || !(FilterPos = (int16_t *)Malloc(sizeof(int16_t)*DestWidth)))
371  {
372  Free(FilterCoeff);
373  Filter->Coeff = NULL;
374  Filter->Pos = 0;
375  Filter->Width = 0;
376  return 0;
377  }
378 
379  Filter->Coeff = FilterCoeff;
380  Filter->Pos = FilterPos;
381  Filter->Width = FilterWidth;
382  MaxPos = SrcWidth - FilterWidth;
383 
384  for(DestX = 0; DestX < DestWidth; DestX++)
385  {
386  SrcX = XStart + XStep*DestX;
387  Pos = (int)ceil(SrcX - KernelRadius);
388 
389  if(Pos < 0 || MaxPos < Pos)
390  {
391  FilterPos[DestX] = CLAMP(Pos, 0, MaxPos);
392 
393  for(n = 0; n < FilterWidth; n++)
394  FilterCoeff[n] = 0;
395 
396  for(n = 0; n < KernelWidth; n++)
397  FilterCoeff[Extension(SrcWidth, Pos + n) - FilterPos[DestX]]
398  += Kernel(SrcX - (Pos + n));
399  }
400  else
401  {
402  FilterPos[DestX] = Pos;
403 
404  for(n = 0; n < FilterWidth; n++)
405  FilterCoeff[n] = Kernel(SrcX - (Pos + n));
406  }
407 
408  if(KernelNormalize) /* Normalize */
409  {
410  Sum = 0;
411 
412  for(n = 0; n < FilterWidth; n++)
413  Sum += FilterCoeff[n];
414 
415  for(n = 0; n < FilterWidth; n++)
416  FilterCoeff[n] /= Sum;
417  }
418 
419  FilterCoeff += FilterWidth;
420  }
421 
422  return 1;
423 }
424 
425 
463 int LinScale2d(float *Dest, int DestWidth, float XStart, float XStep,
464  int DestHeight, float YStart, float YStep,
465  const float *Src, int SrcWidth, int SrcHeight, int NumChannels,
466  float (*Kernel)(float), float KernelRadius, int KernelNormalize,
467  boundaryhandling Boundary)
468 {
469  const int SrcNumPixels = SrcWidth*SrcHeight;
470  const int DestNumPixels = DestWidth*DestHeight;
471  scalescanfilter HFilter = {NULL, 0, 0}, VFilter = {NULL, 0, 0};
472  float *Buf = NULL;
473  int x, y, Channel, Success = 0;
474 
475 
476  if(!Dest || DestWidth <= 0 || DestHeight <= 0 || !Src
477  || SrcWidth <= 0 || SrcHeight <= 0 || NumChannels <= 0
478  || !Kernel || KernelRadius < 0)
479  return 0;
480  if(!(Buf = (float *)Malloc(sizeof(float)*SrcWidth*DestHeight))
481  || !MakeScaleScanFilter(&HFilter, DestWidth, XStart, XStep,
482  SrcWidth, Kernel, KernelRadius, KernelNormalize, Boundary)
483  || !MakeScaleScanFilter(&VFilter, DestHeight, YStart, YStep,
484  SrcHeight, Kernel, KernelRadius, KernelNormalize, Boundary))
485  goto Catch;
486 
487  for(Channel = 0; Channel < NumChannels; Channel++)
488  {
489  for(x = 0; x < SrcWidth; x++)
490  ScaleScan(Buf + x, SrcWidth, DestHeight,
491  Src + x, SrcWidth, VFilter);
492 
493  for(y = 0; y < DestHeight; y++)
494  ScaleScan(Dest + y*DestWidth, 1, DestWidth,
495  Buf + y*SrcWidth, 1, HFilter);
496 
497  Src += SrcNumPixels;
498  Dest += DestNumPixels;
499  }
500 
501  Success = 1;
502 Catch:
503  Free(VFilter.Pos);
504  Free(VFilter.Coeff);
505  Free(HFilter.Pos);
506  Free(HFilter.Coeff);
507  Free(Buf);
508  return Success;
509 }
510 
511 
512 static int FourierScaleScan(float *Dest,
513  int DestStride, int DestScanStride, int DestChannelStride, int DestScanSize,
514  const float *Src,
515  int SrcStride, int SrcScanStride, int SrcChannelStride, int SrcScanSize,
516  int NumScans, int NumChannels, float XStart, double PsfSigma,
517  boundaryhandling Boundary)
518 {
519  const int SrcPadScanSize = 2*(SrcScanSize
520  - ((Boundary == BOUNDARY_WSYMMETRIC) ? 1:0));
521  const int DestPadScanSize = (Boundary == BOUNDARY_HSYMMETRIC) ?
522  (2*DestScanSize)
523  : (2*DestScanSize - floor((2.0f*DestScanSize)/SrcScanSize + 0.5f));
524  const int ReflectOffset = SrcPadScanSize
525  - ((Boundary == BOUNDARY_HSYMMETRIC) ? 1:0);
526  const int SrcDftSize = SrcPadScanSize/2 + 1;
527  const int DestDftSize = DestPadScanSize/2 + 1;
528  const int BufSpatialNumEl = DestPadScanSize*NumScans*NumChannels;
529  const int BufDftNumEl = 2*DestDftSize*NumScans*NumChannels;
530  float *BufSpatial = NULL, *BufDft = NULL, *Modulation = NULL, *Ptr;
531  fftwf_plan Plan = 0;
532  fftwf_iodim Dims[1], HowManyDims[1];
533  float Temp, Denom;
534  int i, Scan, Channel, Success = 0;
535 
536 
537  if((Boundary != BOUNDARY_HSYMMETRIC && Boundary != BOUNDARY_WSYMMETRIC)
538  || !(BufSpatial = (float *)fftwf_malloc(sizeof(float)*BufSpatialNumEl))
539  || !(BufDft = (float *)fftwf_malloc(sizeof(float)*BufDftNumEl)))
540  goto Catch;
541 
542  if(XStart != 0)
543  {
544  if(!(Modulation = (float *)Malloc(sizeof(float)*2*DestDftSize)))
545  goto Catch;
546 
547  for(i = 0; i < DestDftSize; i++)
548  {
549  Temp = M_2PI*XStart*i/SrcPadScanSize;
550  Modulation[2*i + 0] = cos(Temp);
551  Modulation[2*i + 1] = sin(Temp);
552  }
553  }
554 
555  /* Fill BufSpatial with the input and symmetrize */
556  for(Channel = 0; Channel < NumChannels; Channel++)
557  {
558  for(Scan = 0; Scan < NumScans; Scan++)
559  {
560  for(i = 0; i < SrcScanSize; i++)
561  BufSpatial[i + SrcPadScanSize*(Scan + NumScans*Channel)]
562  = Src[SrcStride*i + SrcScanStride*Scan
563  + SrcChannelStride*Channel];
564 
565  for(; i < SrcPadScanSize; i++)
566  BufSpatial[i + SrcPadScanSize*(Scan + NumScans*Channel)]
567  = Src[SrcStride*(ReflectOffset - i)
568  + SrcScanStride*Scan + SrcChannelStride*Channel];
569  }
570  }
571 
572  /* Initialize DFT buffer to zeros (there is no "fftwf_calloc"). Note that
573  it is not safely portable to use memset for this purpose.
574  http://c-faq.com/malloc/calloc.html */
575  for(i = 0; i < BufDftNumEl; i++)
576  BufDft[i] = 0.0f;
577 
578  /* Perform DFT real-to-complex transform */
579  Dims[0].n = SrcPadScanSize;
580  Dims[0].is = 1;
581  Dims[0].os = 1;
582  HowManyDims[0].n = NumScans*NumChannels;
583  HowManyDims[0].is = SrcPadScanSize;
584  HowManyDims[0].os = DestDftSize;
585 
586  if(!(Plan = fftwf_plan_guru_dft_r2c(1, Dims, 1, HowManyDims, BufSpatial,
587  (fftwf_complex *)BufDft, FFTW_ESTIMATE | FFTW_DESTROY_INPUT)))
588  goto Catch;
589 
590  fftwf_execute(Plan);
591  fftwf_destroy_plan(Plan);
592 
593  if(PsfSigma == 0)
594  for(Channel = 0, Ptr = BufDft; Channel < NumChannels; Channel++)
595  for(Scan = 0; Scan < NumScans; Scan++, Ptr += 2*DestDftSize)
596  for(i = 0; i < SrcDftSize; i++)
597  {
598  Ptr[2*i + 0] /= SrcPadScanSize;
599  Ptr[2*i + 1] /= SrcPadScanSize;
600  }
601  else
602  {
603  /* Also divide by the Gaussian point spread function in this case */
604  Temp = SrcPadScanSize / (M_2PI * PsfSigma);
605  Temp = 2*Temp*Temp;
606 
607  for(i = 0; i < SrcDftSize; i++)
608  {
609  if(i <= DestScanSize)
610  Denom = exp(-(i*i)/Temp);
611  else
612  Denom = exp(-((DestPadScanSize - i)*(DestPadScanSize - i))/Temp);
613 
614  Denom *= SrcPadScanSize;
615 
616  for(Channel = 0; Channel < NumChannels; Channel++)
617  for(Scan = 0; Scan < NumScans; Scan++)
618  {
619  BufDft[2*(i + DestDftSize*(Scan + NumScans*Channel)) + 0]
620  /= Denom;
621  BufDft[2*(i + DestDftSize*(Scan + NumScans*Channel)) + 1]
622  /= Denom;
623  }
624  }
625  }
626 
627  /* If XStart is nonzero, modulate the DFT to translate the result */
628  if(XStart != 0)
629  for(Channel = 0, Ptr = BufDft; Channel < NumChannels; Channel++)
630  for(Scan = 0; Scan < NumScans; Scan++, Ptr += 2*DestDftSize)
631  for(i = 0; i < SrcDftSize; i++)
632  {
633  /* Complex multiply */
634  Temp = Ptr[2*i + 0]*Modulation[2*i + 0]
635  - Ptr[2*i + 1]*Modulation[2*i + 1];
636  Ptr[2*i + 1] = Ptr[2*i + 0]*Modulation[2*i + 1]
637  + Ptr[2*i + 1]*Modulation[2*i + 0];
638  Ptr[2*i + 0] = Temp;
639  }
640 
641  /* Perform inverse DFT complex-to-real transform */
642  Dims[0].n = DestPadScanSize;
643  Dims[0].is = 1;
644  Dims[0].os = 1;
645  HowManyDims[0].n = NumScans*NumChannels;
646  HowManyDims[0].is = DestDftSize;
647  HowManyDims[0].os = DestPadScanSize;
648 
649  if(!(Plan = fftwf_plan_guru_dft_c2r(1, Dims, 1, HowManyDims,
650  (fftwf_complex *)BufDft, BufSpatial,
651  FFTW_ESTIMATE | FFTW_DESTROY_INPUT)))
652  goto Catch;
653 
654  fftwf_execute(Plan);
655  fftwf_destroy_plan(Plan);
656 
657  /* Fill Dest with the result (and trim padding) */
658  for(Channel = 0; Channel < NumChannels; Channel++)
659  {
660  for(Scan = 0; Scan < NumScans; Scan++)
661  {
662  for(i = 0; i < DestScanSize; i++)
663  Dest[DestStride*i + DestScanStride*Scan
664  + DestChannelStride*Channel]
665  = BufSpatial[i + DestPadScanSize*(Scan + NumScans*Channel)];
666  }
667  }
668 
669  Success = 1;
670 Catch:
671  Free(Modulation);
672  if(BufDft)
673  fftwf_free(BufDft);
674  if(BufSpatial)
675  fftwf_free(BufSpatial);
676  fftwf_cleanup();
677  return Success;
678 }
679 
680 
705 int FourierScale2d(float *Dest, int DestWidth, float XStart,
706  int DestHeight, float YStart,
707  const float *Src, int SrcWidth, int SrcHeight, int NumChannels,
708  double PsfSigma, boundaryhandling Boundary)
709 {
710  float *Buf = NULL;
711  int Success = 0;
712 
713  unsigned long StartTime, StopTime;
714 
715 
716  if(!Dest || DestWidth < SrcWidth || DestHeight < SrcHeight || !Src
717  || SrcWidth <= 0 || SrcHeight <= 0 || NumChannels <= 0 || PsfSigma < 0
718  || !(Buf = (float *)Malloc(sizeof(float)*SrcWidth*DestHeight*3)))
719  return 0;
720 
721  StartTime = Clock();
722 
723  /* Scale the image vertically */
724  if(!FourierScaleScan(Buf, SrcWidth, 1, SrcWidth*DestHeight, DestHeight,
725  Src, SrcWidth, 1, SrcWidth*SrcHeight, SrcHeight,
726  SrcWidth, 3, YStart, PsfSigma, Boundary))
727  goto Catch;
728 
729  /* Scale the image horizontally */
730  if(!FourierScaleScan(Dest, 1, DestWidth, DestWidth*DestHeight, DestWidth,
731  Buf, 1, SrcWidth, SrcWidth*DestHeight, SrcWidth,
732  DestHeight, 3, XStart, PsfSigma, Boundary))
733  goto Catch;
734 
735  StopTime = Clock();
736  printf("CPU Time: %.3f s\n\n", 0.001*(StopTime - StartTime));
737 
738  Success = 1;
739 Catch:
740  Free(Buf);
741  return Success;
742 }