When processing noisy data, it is usually beneficial to filter high frequencies, this can be done with a small kernel as shown in answer by Dan Boschen.
Also if your data has an envelope it will give you some high intensity low frequencies, the low frequencies require a long kernel to be filtered, and it is better to do in the frequency domain.
The topic of filter design has been extensively explored in the past but for 1D filtering (time domain). I use the 2D information to construct a direction independent filter.
Also, since the idea of this filtering is just to emphasize certain frequencies and we don't need to implement the transfer function in any recurrence equation, I just multiply the magnitude of the filter leaving the phase unchanged (I know your problem).
img = img - mean(img(:));
IMG = fft2(img);
[M, N] = size(IMG);
figure, imagesc(abs(IMG(1:64, 1:64)));
The frequencies you are interesteed in are problably those in the second spot close to 10 in the vertical axis. But we have a some frequencies that are detected higher represented at the top left corner of the image. Those need to be attenuated so that you can capture the frequencies of interest.
I will create a transfer function for a wide bandpass filter by cascading a
a high pass filter with cutoff in 10/v, and a lowpass filter at 200/v
The low frequencies are due to the envelope
f1 = 10/(N+M);;
f2 = 200/(N+M);
H = @(f) abs(f1 ./ (f1 + 1j*f) .* (1j.*f) ./ (1j*f + f2));
% see the filter response
x = linspace(0, 0.5, 1000);
figure, loglog(x, H(x));
Assuming each pixel corresponds to a square in the space (a rectanble with width = height) we can compute a frequency measured in cycles per pixel (where pixel corresponds the physical distance that separates two pixels)
% Spatial frequencies measured in cycles per pixel in each direction
phi_y = angle(exp(2j*pi*(0:M-1)/M))/(2*pi);
phi_x = angle(exp(2j*pi*(0:N-1)/N))/(2*pi);
% Construct a matrix of freq
F = sqrt(phi_y'.^2 + phi_x.^2);
If you have the actual length of each pixel you can simply divide
phi_y by the corresponding lengths.
As I said we have two options, one is to apply the analytic filter, but that changes the phase, or simply apply a real filter, by multiplying the magnitude.
Let's double check if this filtering does not modify too much the images by plotting them.
imagesc(real(ifft2(IMG .* H(F))));
imagesc(imag(ifft2(IMG .* H(F))));
imagesc(real(ifft2(IMG .* abs(H(F))))), colormap('gray'), colorbar();
imagesc(imag(ifft2(IMG .* abs(H(F))))), colormap('gray'), colorbar();
In my opinion the real filter is good, it does not leak the signal to the imaginary part and keeps the appearance of the image the same, the information you want is still there. If you want want you can increase f1 so that it filters the low frequencies even more, and you could for instance multipy by the square of the filter magnitude.
Now the last thing is to check if the peak we expect is the higher than the ones at the top left corner.
figure, imagesc(abs(IMG .* H(F))(1:64, 1:64)), colorbar();