How to use nufftn() for interpolation?

60 visualizaciones (últimos 30 días)
Jerry Guern
Jerry Guern el 16 de Oct. de 2024
Comentada: Jerry Guern el 20 de Nov. de 2024 a las 22:15
The documentation for nufftn() is very unclear, and neither of the Q/A's I found here give me enough info.
I put together a toy problem that illustrates what I'm attempting to do, that I think I should be able to do with nufftn(). The code below generates 100 scattered sample points x,y and 100 corresponding values of a smooth function v(x,y). I'd like to use a non-uniform discreet fft to convert those 100 scattered samples into a nice square 10x10 grid of interpolated values. I thought nufftn() would give me a 10x10 of frequency coefficients, but apparently not. The t doesn't come out the grid of gaussian values I expected. So, what must I do to make this function do what I was trying to do? Thanks!
EDIT : I rewrote my code as recommended in the Answer below, but something is still off:
x = rand(100,1);
y = rand(100,1); % Random sampling pts in 2D
v = exp(-x.^2-y.^2); % Values at sample points
t = {x,y};
[Xq, Yq] = meshgrid(linspace(0, 1, 10), linspace(0, 1, 10));
f = {Xq(:), Yq(:)};
c = nufftn(v,t,f);
p = reshape(c,[10,10]); % I thought is would be 10x10 freq values...
surf(Xq,Yq,p);
% If this code did what I'm trying, p would be approx exp(-(.05:.1:.95).^2-(.05:.1:.95)'.^2)
Here's the result:
Error using nufftn (line 107)
The total number of sample points must match the number of elements in the first input argument.
Error in ttt (line 8)
c = nufftn(v,t,f);
But if I change the curly brackets in Lines 5&7 to square:
t=[x,y];
[Xq, Yq] = meshgrid(linspace(0, 1, 10), linspace(0, 1, 10));
f = [Xq(:), Yq(:)];
...then I get big complex numbers for c and p. The abs(p) does have the Gaussian-ish shape I expected, but the values are complex and way off from the values of v.
I also tried oversampling, by changing the 100's in my first two lines to 10000, and it ran okay, but I get the same result of huge complex values in p with gaussian-ish magnitudes.
So, this is still just not quite doing what I wanted. Thanks in advance for help.
My old code in my original post:
x = rand(100,1);
y = rand(100,1); % Random sampling pts in 2D
v = exp(-x.^2-y.^2); % Values at sample points
c = nufftn(v,[x y]); % ???
d = reshape(c,[10,10]); % I thought is would be 10x10 freq values...
t = ifft2(d); % ...but apparently not.

Respuesta aceptada

Jerry Guern
Jerry Guern el 20 de Nov. de 2024 a las 22:12
Okay, so it turns out that
1) My method is non-ideal for this specific problem, but that's what I needed to find out.
2) nufftn() can totally do a successful FFT on random points, which was the goal of my Question
3) The nufftn() documentation available at the time I posted this doesn't really tell the story, and I had to get help from @Chris Turnes at Mathworks to get this to work.
The code below displays a smooth function and my attempt to estimate it from random samples using nufftn(). The range and overall shape of the estimate comes out right on, but it's very noisy, becaue the random samples aren't uniform and therefore aren't great for estimating Fourier Transform coefficients. And it takes major oversampling to make up for the randomness. But with some serious smoothing, this estimate would be not-too-bad, and this approach might be viable-ish for certain problems.
dim1 = 21; % Dimensions of output grid. Make these odd.
dim2 = 23;
nsample = 200000; % Number of random samples
oversample = nsample / (dim1 *dim2);
xs = rand(nsample,1) - 1/(2*dim1); % Random sampling pts in 2D
ys = rand(nsample,1) - 1/(2*dim2);
v = exp(-xs.^2-ys.^2); % Values at sample points
% Wave numbers for Fourier Transform
kx = [0 [1:floor(dim1/2)] [-floor(dim1/2) : -1]];
ky = [0 [1:floor(dim2/2)] [-floor(dim2/2) : -1]]';
pfd2 = reshape(nufftn(v, [ys(:) xs(:)], { ky kx }), dim2, dim1) / oversample;
estimate = ifft2(pfd2); % Our attempt at estimating v
[Xq, Yq] = meshgrid(linspace(0,1-1/(dim1),dim1),linspace(0,1-1/(dim2),dim2));
figure(50); % Display our estimate of v
surf(Xq,Yq,estimate);
title_str = sprintf('ifft2 of nufftn()\n%d random data pts\noversampling = %.2f',nsample,oversample);
title(title_str);
figure(51);
surf(Xq,Yq,exp(-Xq.^2-Yq.^2)); % Display the actual v
title('The actual function, for comparison');
  1 comentario
Jerry Guern
Jerry Guern el 20 de Nov. de 2024 a las 22:15
I haven't checked yet, but I would bet money that the lumpiness of this result would mirror the clumpiness of the randome sample points.

Iniciar sesión para comentar.

Más respuestas (2)

Chris Turnes
Chris Turnes el 17 de Oct. de 2024
Editada: Chris Turnes el 17 de Oct. de 2024
There are a few things that are probably worth explaining to answer your question. The key points are really:
  • when using non-uniform transforms, you have to be careful about managing the scaling of the grid, and
  • nufft is not an inverse of fft. The more non-uniform the sampling modalities get, the further and further you get from being able to get something close to the theoretical result of the signal from its original samples by using nufft and fft/ifft back to back.
That said, it might be helpful to fully explain things by beginning with a 1-D version. The kernel you're considering is the exponential function
Definition of a kernel v that is the multiplication of two exponential-of-squares
This is a separable kernel, so we can simplify things by just reducing to 1 dimension:
One-dimensional version of the previous kernel
The generalized m-point non-uniform DFT is given by
Non-uniform DFT equation for the kernel in the question
where the f(i) are whatever frequencies at which you choose to evaluate the transform. The default query points when you don't supply them are to take m = n and f = (0:(n-1))/n. So let's evaluate that:
% Set to a known random state.
rng('default');
n = 10;
x = rand(n,1);
v = exp(-x.^2);
V = nufft(v, x);
fdefault = (0:(n-1))/n;
Vref = exp(-1j*2*pi*fdefault(:)*x') * v;
norm(V-Vref)
ans = 3.3471e-14
So now let's go back to the original question. From the documentation for nufftn:
"When no query points are specified, the transform is computed at N evenly spaced query points in each dimension, where N = ceil(numel(X).^(1/D)) and D is the number of columns in t."
This means we expect a 10 x 10 grid that's an analog of what we had in 1-D, so let's see how it compares:
rng('default');
n = 10;
x = rand(n^2,1);
y = rand(n^2,1); % Random sampling pts in 2D
v = exp(-x.^2-y.^2); % Values at sample points
V = nufftn(v,[x y]);
fdefault = (0:(n-1))/n;
% Create the frequency grids for the row dimension and column dimension:
f1 = kron(ones(n,1), fdefault(:));
f2 = kron(fdefault(:), ones(n,1));
Vref = exp(-1j*2*pi*(f1*x(:)' + f2*y(:)'))*v;
norm(V-Vref)
ans = 1.2920e-13
So from this you should be able to tell what your frequency grid is -- it is the 10 x 10 grid with nodes (0:(n-1))/n in each dimension.
Now let's revisit your goal, and again to simplify things let's go to 1-D. What would happen if x was uniformly spaced?
% Set to a known random state.
rng('default');
n = 10;
x = 0:0.1:0.9;
v = exp(-x.^2);
V = nufft(v, x);
vr = ifft(V);
You'd think since we've uniformly sampled the function that this should give us back v right? Not exactly:
max(abs(vr - v), [], 'all')
ans = 3.8703
The issue here is grid scaling. nufft(g, x) is not the same thing as fft(g) -- if we wanted it to be equivalent, we'd have to scale x
norm(nufft(v,x*10) - fft(v))
ans = 0
or the query points
norm(nufft(v,x,0:9) - fft(v))
ans = 0
This is because of the grid scaling in the FFT. Typically, we think of the FFT as either taking sample points 0:(n-1) and query points (0:(n-1))/n or vice versa. You can consider it either way, or any other scaling such that the product of the ranges of values for the two equals just below n. So given that, let's consider the 1-D case again, and let's imagine you have ideal, uniformly-spaced samples:
n = 10;
x = (0:(n-1))'/n;
v = exp(-x.^2);
vr = ifft(nufft(v, n*x));
vt = exp(-((0:(n-1))/n).^2)';
norm(vr-vt)
ans = 4.3356e-16
As we move from uniform samples to non-uniform samples, you're going to start seeing the effects of what non-uniformity in sampling does. Let's move the samples just a tiny bit:
rng('default');
x = (0:(n-1))'/n + 1e-6*randn(n,1);
v = exp(-x.^2);
vr = ifft(nufft(v, n*x));
norm(vr-vt)
ans = 1.3277e-04
We've really not moved the samples by much, but already we're a good bit away from the "idealized" answer. Jitter the samples more and you'll see the effect take off:
rng('default');
x = (0:(n-1))'/n + 1e-3*randn(n,1);
v = exp(-x.^2);
vr = ifft(nufft(v, n*x));
norm(vr-vt)
ans = 0.1338
And if you get to the equivalent of the original example, then you've really lost most everything:
rng('default');
x = randn(n,1);
v = exp(-x.^2);
vr = ifft(nufft(v, n*x));
norm(vr-vt)
ans = 2.3695
This happens because the more non-uniform the samples get, the further and further the non-uniform DFT matrix gets from being the inverse of the (inverse) Fourier transform matrix.
So, in summary:
  • Part of the reason the results are so different from what you expect is because you need to apply some grid scaling.
  • Even with the grid scaling, though, the non-uniformity is severe enough that you're unlikely to see something that really looks like an exponential.
I'm not sure what your larger goal is, but typically folks use iterative methods to try to do this type of interpolation onto uniform samples with a degree of accuracy. You can also try interpolating onto a coarser grid and using "density compensation" for how your non-uniform samples are scattered.

Umar
Umar el 17 de Oct. de 2024

Hi @Jerry Guern ,

After going through documentation provided at the link below,

https://www.mathworks.com/help/matlab/ref/double.nufftn.html

Let me break down the steps required and clarify the misunderstandings:

The nufftn( )function computes the NUDFT based on provided sample points and optionally, query points. Your goal is to convert your scattered samples into a structured grid, which requires careful setup of both the input data and the target grid. You have 100 random sample points (x, y) and corresponding values v. Make sure that x and y are properly formatted as matrices or vectors that correspond to the shape of your input data. The vector v should be a column vector of size 100, containing the values at each sample point. So, you need to specify the sample points correctly when calling nufftn( ). Instead of passing [x y], you should provide a cell array where each dimension's sample points are specified separately. For example:

     t = {x, y};  % Cell array containing x and y as separate entries

To get a grid output, you need to define query points that correspond to the desired grid structure. For a 10x10 grid, create evenly spaced points within the range of your original data.For instance:

     [Xq, Yq] = meshgrid(linspace(0, 1, 10), linspace(0, 1, 10));  % Create query 
     points
     f = {Xq(:), Yq(:)};  % Reshape them into vectors for nufftn

At this point, you can call nufftn() using both your sample points and your query points:

     c = nufftn(v, t, f);  % Compute NUDFT at query points

This will give you frequency coefficients at your specified grid locations. Since you will receive coefficients in an array format corresponding to your query points, you may not need to reshape it into a square grid directly unless required for further processing. If you want to visualize it as a grid or perform an inverse transform (like in your original code), ensure that you handle dimensions appropriately. The resulting variable c will contain your interpolated values at the specified grid points. If you want to visualize this output or compare it with expected Gaussian values:

     t = reshape(c, [10, 10]);  % Reshape if necessary for visualization
     surf(Xq, Yq, t);            % Visualize using surface plot

The expectation that the output resembles Gaussian values is valid; however, remember that interpolation will depend heavily on how well your scattered samples represent the underlying function across the defined domain.

Hope this helps.

If further issues arise or if additional clarifications are needed on specific aspects of this process, feel free to ask!

  12 comentarios
Jerry Guern
Jerry Guern el 19 de Nov. de 2024 a las 14:59
Chris, I posted a related Question that's more open-ended and probably within your wheelhouse. Would you mind taking a look? I'm trying to do an FFT on a set of sample points that are uniform but on a slanted paralellogram grid. I thought this was something nufftn() could do, but you convinced me it's not. Thanks in advance. https://www.mathworks.com/matlabcentral/answers/2167093-can-matlab-do-a-2-d-fft-on-a-uniform-parallelogram-grid-of-measured-points
Chris Turnes
Chris Turnes el 19 de Nov. de 2024 a las 21:02
@Jerry Guern -- I've answered on your other post, and indeed nufftn can help you compute what you want. Your other question is a bit different from the one you've posed here, and is well-suited to what nufftn can do.

Iniciar sesión para comentar.

Productos


Versión

R2024a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by