Nicolas
Nicolas

Reputation: 55

FFTW with MEX and MATLAB argument issues

I wrote the following C/MEX code using the FFTW library to control the number of threads used for a FFT computation from MATLAB. The code works great (complex FFT forward and backward) with the FFTW_ESTIMATE argument in the planner although it is slower than MATLAB. But, when I switch to the FFTW_MEASURE argument to tune up the FFTW planner, it turns out that applying one FFT forward and then one FFT backward does not return the initial image. Instead, the image is scaled by a factor. Using FFTW_PATIENT gives me an even worse result with null matrices.

My code is as follows:

Matlab functions:

FFT forward:

function Y = fftNmx(X,NumCPU)   

if nargin < 2
    NumCPU = maxNumCompThreads;
    disp('Warning: Use the max maxNumCompThreads');
end
Y = FFTN_mx(X,NumCPU)./numel(X);

FFT backward:

function Y = ifftNmx(X,NumCPU)

if nargin < 2
    NumCPU = maxNumCompThreads;
    disp('Warning: Use the max maxNumCompThreads');
end

Y = iFFTN_mx(X,NumCPU);

Mex functions:

FFT forward:

# include <string.h>
# include <stdlib.h>
# include <stdio.h>
# include <mex.h>
# include <matrix.h>
# include <math.h>
# include </home/nicolas/Code/C/lib/include/fftw3.h>

char *Wisfile = NULL;
char *Wistemplate = "%s/.fftwis";
#define WISLEN 8

void set_wisfile(void)
{
    char *home;
    if (Wisfile) return;
    home = getenv("HOME");
    Wisfile = (char *)malloc(strlen(home) + WISLEN + 1);
    sprintf(Wisfile, Wistemplate, home);
}


fftw_plan CreatePlan(int NumDims, int N[], double *XReal, double *XImag, double *YReal, double *YImag)
{
  fftw_plan Plan;
  fftw_iodim Dim[NumDims];
  int k, NumEl;
  FILE *wisdom;

  for(k = 0, NumEl = 1; k < NumDims; k++)
  {
    Dim[NumDims - k - 1].n = N[k];
    Dim[NumDims - k - 1].is = Dim[NumDims - k - 1].os = (k == 0) ? 1 : (N[k-1] * Dim[NumDims-k].is);
    NumEl *= N[k];
  }

/* Import the wisdom. */
  set_wisfile();
  wisdom = fopen(Wisfile, "r");
  if (wisdom) {
    fftw_import_wisdom_from_file(wisdom);
    fclose(wisdom);
  }

  if(!(Plan = fftw_plan_guru_split_dft(NumDims, Dim, 0, NULL, XReal, XImag, YReal, YImag, FFTW_MEASURE *(or FFTW_ESTIMATE respectively)* )))
    mexErrMsgTxt("FFTW3 failed to create plan.");

/* Save the wisdom.  */
  wisdom = fopen(Wisfile, "w");
  if (wisdom) {
    fftw_export_wisdom_to_file(wisdom);
    fclose(wisdom);
  }

  return Plan;
}


void mexFunction( int nlhs, mxArray *plhs[],
              int nrhs, const mxArray *prhs[] )
{
  #define B_OUT     plhs[0]

  int k, numCPU, NumDims;
  const mwSize *N;
  double *pr, *pi, *pr2, *pi2;
  static long MatLeng = 0;
  fftw_iodim Dim[NumDims];
  fftw_plan PlanForward;
  int NumEl = 1;
  int *N2;

  if (nrhs != 2) {
      mexErrMsgIdAndTxt( "MATLAB:FFT2mx:invalidNumInputs",
                "Two input argument required.");
  }

  if (!mxIsDouble(prhs[0])) {
      mexErrMsgIdAndTxt( "MATLAB:FFT2mx:invalidNumInputs",
                "Array must be double");
  }

  numCPU = (int) mxGetScalar(prhs[1]);
  if (numCPU > 8) {
      mexErrMsgIdAndTxt( "MATLAB:FFT2mx:invalidNumInputs",
                "NumOfThreads < 8 requested");
  }

  if (!mxIsComplex(prhs[0])) {
      mexErrMsgIdAndTxt( "MATLAB:FFT2mx:invalidNumInputs",
                "Array must be complex");
  }


  NumDims = mxGetNumberOfDimensions(prhs[0]);
  N = mxGetDimensions(prhs[0]);
  N2 = (int*) mxMalloc( sizeof(int) * NumDims);
  for(k=0;k<NumDims;k++) {
    NumEl *= NumEl * N[k];
    N2[k] = N[k];
  }

  pr = (double *) mxGetPr(prhs[0]);
  pi = (double *) mxGetPi(prhs[0]);

  //B_OUT = mxCreateNumericArray(NumDims, N, mxDOUBLE_CLASS, mxCOMPLEX);
  B_OUT  = mxCreateNumericMatrix(0, 0, mxDOUBLE_CLASS, mxCOMPLEX);
  mxSetDimensions(B_OUT , N, NumDims);
  mxSetData(B_OUT , (double* ) mxMalloc( sizeof(double) * mxGetNumberOfElements(prhs[0]) ));
  mxSetImagData(B_OUT , (double* ) mxMalloc( sizeof(double) * mxGetNumberOfElements(prhs[0]) ));

  pr2 = (double* ) mxGetPr(B_OUT);
  pi2 = (double* ) mxGetPi(B_OUT);

  fftw_init_threads();
  fftw_plan_with_nthreads(numCPU);  
  PlanForward = CreatePlan(NumDims, N2, pr, pi, pr2, pi2);
  fftw_execute_split_dft(PlanForward, pr, pi, pr2, pi2);
  fftw_destroy_plan(PlanForward);
  fftw_cleanup_threads();

}

FFT backward:

This MEX function differs from the above only in switching pointers pr <-> pi, and pr2 <-> pi2 in the CreatePlan function and in the execution of the plan, as suggested in the FFTW documentation.

If I run

A = imread('cameraman.tif');
>> A = double(A) + i*double(A);
>> B = fftNmx(A,8);
>> C = ifftNmx(B,8);
>> figure,imagesc(real(C))

with the FFTW_MEASURE and FFTW_ESTIMATE arguments respectively I get this result.

I wonder if this is due to an error in my code or in the library. I tried different thing around the wisdom, saving not saving. Using the wisdom produce by the FFTW standalone tool to produce wisdom. I haven't seen any improvement. Can anyone suggest why this is happening?

Additional information:

I compile the MEX code using static libraries:

mex FFTN_Meas_mx.cpp /home/nicolas/Code/C/lib/lib/libfftw3.a /home/nicolas/Code/C/lib/lib/libfftw3_threads.a -lm

The FFTW library hasn't been compiled with:

./configure  CFLAGS="-fPIC" --prefix=/home/nicolas/Code/C/lib --enable-sse2 --enable-threads --&& make && make install

I tried different flags without success. I am using MATLAB 2011b on a Linux 64-bit station (AMD opteron quad core).

Upvotes: 1

Views: 1090

Answers (1)

Ricky
Ricky

Reputation: 56

FFTW computes not normalized transform, see here: http://www.fftw.org/doc/What-FFTW-Really-Computes.html

Roughly speaking, when you perform direct transform followed by inverse one, you get back the input (plus round-off errors) multiplied by the length of your data.

When you create a plan using flags other than FFTW_ESTIMATE, your input is overwritten: http://www.fftw.org/doc/Planner-Flags.html

Upvotes: 4

Related Questions