FFT giving undesired answer
Show older comments
I'm trying to do a periodic analysis of the horizontal rows of holes on this image:

I've begun by projecting the white pixels onto the y-axis and plotting those values:

My goal is to determine the frequency of the peaks, so I went ahead with applying a fft of this data to acquire the fourier coefficients, computing the power and frequency from these (I learned from another MATLAB tutorial that power is just the magnitude of the fourier coefficients?), and plotting 1/frequency (period) against power. Finally, I believe the period corresponding to the max power should give the period that I want.
Since I've been assembling this code from examples I've seen on this forum, there are two lines I don't understand (and that I think are contributing to the issue): the lines where I calculate the maxfreq and freq. It seems that I'm trying to create a distribution of frequencies to correspond to the powers that I've calculated, but what role does maxfreq have? It seems to spread out the data in the x-direction such that I can find the maxfreq which gives any answer I want... so how do I determine what that is? Any guidance on those lines and the issue in general would be greatly appreciated!!
For reference, the code below currently gives that the vertGridSpacing is 59.94 pixels, while the true vertGridSpacing should be 123 pixels.
% Read image
bwmask = imread('bwmask.png');
% Project white pixels of bwmask onto y-axis
y_proj = sum(bwmask, 2);
x = linspace(1,length(y_proj),length(y_proj));
% Calculate fourier coefficients and eliminate first value (since it's just a sum)
tform = fft(y_proj);
tform(1) = [];
% Calculate the power corresponding to first half of fourier coefficients (since they're real)
n = length(tform);
power = abs(tform(1:floor(n/2))).^2;
% Create equally spaced frequency grid (this is the part I don't understand)
maxfreq = 1;
freq = (1:n/2)/(n/2)*maxfreq;
% Determine vertical grid spacing by taking period corresponding to maximum power
period = 1./freq;
[~, sortID] = sort(power,'descend'); % sort by x-value first
vertGridSpacing = period(sortID(1));
6 Comments
John D'Errico
on 5 Jul 2022
I always laugh whenever I see someone telling us FFT is giving the wrong answer or there is a bug in double precision arithmetic, etc.
FFT is giving the answer it is designed to return. You may be interpreting the result of what you passed in incorrectly.
Cole Meyer
on 5 Jul 2022
Edited: Cole Meyer
on 5 Jul 2022
Paul
on 5 Jul 2022
Hi Cole,
Posting the file bwmask.png will allow you to get more help because then people can run your code and/or post their own solution for the same input image.
You can edit the Question and click the paper clip icon in the "Insert" tools if you want to go down this path.
BTW, if all you want to do is pull out the primary frequency (or period) there's no reason to bother with squaring the coefficients, which is actually energy, nor the one-sided stuff. Just plotting the magnitude of the coefficients vs the full frequency vector should be all that is needed for this problem.
Cole Meyer
on 5 Jul 2022
Edited: Cole Meyer
on 5 Jul 2022
John D'Errico
on 5 Jul 2022
@Paul Actually, you can just save the image shown, then attach it, as I did. Then you can use the image too, since MATLAB online can see and use attachments.
John D'Errico
on 5 Jul 2022
@Cole Meyer, it looks like your real goal is to understand how to use fft to recover the period, not to find the period itself.
Accepted Answer
More Answers (1)
Hi Cole,
As you've already accepted an answer, perhaps you already have what you need. But I thought it might be helpful to add this because the results that I get aren't quite the same as what you get.
Read in the image, I assume the warning doesn't affect anything.
bwmask = imread('https://www.mathworks.com/matlabcentral/answers/uploaded_files/1056135/bwmask.png');
% Project white pixels of bwmask onto y-axis
y_proj = sum(bwmask, 2);
figure
plot(0:(numel(y_proj)-1),y_proj)
Take the DFT
Y = fft(y_proj);
The corresponding spatial frquency vector is, assuming a sampling period of 1 pixel/sample
f = (0:(numel(Y)-1))/numel(Y); % samples/pixel
Plot the magnitude of the DFT vs. 1/f and add a line at the expected period of 123 pixels
figure
stem(1./f,abs(Y))
xline(123,'r')
xlim([100 140])
The plot shows a peak at 120 pixels (not 119.8?) and suggests an actual peak maybe between 105-140 pixels, but the spatial sampling and number of points is enough to narrow it down more than that.
Y = fft(y_proj,8192);
f = (0:(numel(Y)-1))/numel(Y); % samples/pixel
figure
stem(1./f,abs(Y))
xline(123,'r')
xlim([115 130])
Now we see the DFT "closing in" on 123 pixels, but at the end of the day you'll still have to decide how to pick the best single period for what you're trying to do. After all, as you, and @John D'Errico, have already shown, the peaks aren't equally spaced, if only by a few pixels, so there really isn't a single "period."
Categories
Find more on Image Filtering in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!



