1

I am attempting to calculate the MTF from a test target. I calculate the spread function easily enough, but the FFT results do not quite make sense to me. To summarize,the values seem to alternate giving me a reflection of what I would expect. To test, I used a simple square wave and numpy:

from numpy import fft

data = []
for x in range (0, 20):
    data.append(0)

data[9] = 10
data[10] = 10
data[11] = 10

dataFFT = fft.fft(data)

The results look correct, with the exception of the sign... I am seeing the following for the first 4 values as an example:

30.00000000 +0.00000000e+00j

-29.02113033 +7.10542736e-15j

26.18033989 -1.24344979e-14j

-21.75570505 +1.24344979e-14j

So my question is why positive->negative->positive->negative in the real plane? This is not what I would expect... It I plot it, it almost appears that the correct function is mirrored around the x axis.

Note: I was expecting the following as an example: example image

This is what I am getting: My results

Dan
  • 175
  • 2
  • 12
  • 1. added answer see bullet 1. it is most likely the source of your confusion. 2. what is MTF ??? – Spektre Oct 03 '14 at 06:23
  • The result that you're expecting probably corresponds to `data = zeros(20); data[-1] = 10; data[:2] = 10`, i.e. a source signal that's symmetric about index 0 (the DFT assumes the source is periodic). The signal you analyzed instead delays that by 10 samples. – Eryk Sun Oct 03 '14 at 08:52
  • Consider the signal component at the mth frequency: `s(m,n) = A_m * cos(pi*m/10*n)`. Delay this by 10 samples: `s(m,n-10) = A_m * cos(pi*m/10*n - pi*m)`. So the mth frequency component is shifted by `pi*m` radians, i.e. multiply the DFT vector by `exp(1j*pi*m) == [1, -1, 1, -1, ...]`. – Eryk Sun Oct 03 '14 at 08:59
  • Spektre: Thanks for the answer, MTF is the Modulation Transfer Function (http://www.trioptics.com/knowledgebase/mtf.php). It is commonly used in image quality evaluations. I often use tools to calculate it but find myself in a position where I need to create my own implementation to operate on some non-standard hardware. – Dan Oct 04 '14 at 01:08
  • @dan you have to add `@nickname` to make the comment notification to work for example if you typed `@Spektre` instead of `Spektre:` then I would be notified of your new comment. – Spektre Oct 15 '14 at 07:30

4 Answers4

3

Your pulse is symmetric and positioned in the center of your FFT window (around N/2). Symmetric real data corresponds to only the cosine or "real" components of an FFT result. Note that the cosine function alternates between being -1 and 1 at the center of the FFT window, depending on the frequency bin index (representing cosine periods per FFT width). So the correlation of these FFT basis functions with a positive going pulse will also alternate as long as the pulse is narrower than half the cosine period.

If you want the largest FFT coefficients to be mostly positive, try centering your narrow rectangular pulse around time 0 (or circularly, time N), where the cosine function is always 1 for any frequency.

hotpaw2
  • 70,107
  • 14
  • 90
  • 153
  • 1
    Centering on index 0 gives the sinc the OP is probably expecting, instead of the `(-1)**n` modulated result, but the coefficients aren't all positive. It's a sinc! For example, the bin at pi radians/sample correlates as [-1*10, 1*10, -1*10], which sums to -10. – Eryk Sun Oct 03 '14 at 09:16
  • Absolutely correct about the Sinc function. A narrow rectangular pulse produces a wide Sinc. So I updated my answer. – hotpaw2 Oct 03 '14 at 11:07
  • I added an example to the question above. I also tried moving to origin. The plot does indeed appear to match. Would this apply to my line spread function? Just take half the data? It would seem to work, but it also changes the results. I will have to test the results experimentally anyhow, so I can verify, but I am not in the lab. – Dan Oct 04 '14 at 01:15
  • @Dan, shift the center to index 0, e.g. `numpy.roll(data, 10)`. This delays the signal such that the cosine component with frequency `m*pi/10` is shifted by `m*pi` radians, where m is an integer, thus scaling the DFT by `[1, -1, 1, -1, ...]`. This scaling results in the real-valued sinc you're expecting. Other delays have a complex-valued DFT because the resulting waveform isn't even-symmetric (cosine-like), but the magnitude response (i.e. `abs(dataFFT)`) is the same regardless. – Eryk Sun Oct 04 '14 at 05:59
  • Yes, this gives me the results I was expecting! Thanks! – Dan Oct 05 '14 at 12:01
2

It works if you shift the data around 0 instead of half your array, with:

dataFFT = fft.fft(np.fftshift(data))
Cris Luengo
  • 55,762
  • 10
  • 62
  • 120
Pierre
  • 21
  • 1
1

This isn't all that unexpected. If you want to check against conventional plots, make sure you convert that info to magnitude and phase before coming to any conclusions.

I did a quick check using your code and numpy.abs for mag, numpy,angle for phase. It sure looks like a sinc() function to me, which is what would be expected if the time-domain is a square pulse. If you do this, you'll find a pretty wide sinc, as would be expeceted for a short duration pulse on so few samples.

jee7s
  • 57
  • 4
  • I added a sample from one of my reference books. The spectral function matches nearly perfectly in my implementation (taking into account magnitude and phase). However, as you can see in the second waveform does not quite match what I am seeing in the quick example. Thanks for the help! – Dan Oct 04 '14 at 01:11
1
  1. you forget to specify if your data is Real or Complex

    not everyone code in python/numpy (including me) and if you do not know this then you probably handle data to/from FFT the wrong way.

    • FFT input can be both real or complex domain
    • FFT output is complex domain

    so check the docs for your FFT implementation and specify it and also repair your data handling accordingly. Complex domain usually have first value Re and Second Im but that depends on FFT implementation/configuration.

  2. signal

    here is an example of impulse response from FFT FFT impulse response first is input Real domain signal (Im=0) single finite nonzero width pulse and second is the Re part of FFT output. The third is the Im part of FFT output. If you zoom it a bit then you will see amplitude range of y axis of each signal (on left).

    Do not forget that different FFT implementations can have different normalization constants which will change the amplitude of signal. If you want magnitude and phase convert it like this:

    mag=sqrt(Re*Re+Im*Im); // power
    ang=atanxy(Re,Im); // phase angle
    

    atanxy(dx,dy) is 4 quadrant arctan also called atan2 but be careful to get the operand order the same as your atanxy/atan2 implementation needs. Also can use mine C++ atanxy implementation

[Notes]

if your input signal is Real domain then FFT output is symmetric. Both Re and Im signals will be like:

{ a0,a1,a2,a3,...,a(n-1),a(n-1)...,a3,a2,a1,a0 }

exactly like on the image above. On the left are low frequencies and in the middle is the top frequency. If your input signal is Complex domain then the output can be anything.

Community
  • 1
  • 1
Spektre
  • 49,595
  • 11
  • 110
  • 380