What's the correct way to shift zero frequency to the center of a Fourier Transform?

The Fourier transform is defined as:

$$ \begin{align*} H(f) &= \int_{-\infty}^\infty h(t) \mathrm e^{2\pi \mathrm i f t}\mathrm dt\\ h(t) &= \int_{-\infty}^\infty H(f) \mathrm e^{-2\pi \mathrm i f t}\mathrm df \end{align*} $$

where $ h(t) $ is the signal, and $ H(f) $ is its Fourier transform; if $ t $ is measured in seconds, then $ f $ is measured in hertz (Hz).

The discrete Fourier transform is defined as:

$$ \begin{align*} H_{f_j} &= \frac{1}{N}\sum_{k}h_{t_k}\mathrm e^{2\pi \mathrm i f_j t_k}\\ h_{t_j} &= \frac{1}{N}\sum_{k}H_{f_k}\mathrm e^{-2\pi \mathrm i f_k t_j}\\ \end{align*} $$

where the $ t_k $ are the time corresponding to my signal in the time domain $ h_{t_k} $, $ f_k $ are the corresponding frequency to my signal in the frequency domain, and $ N $ is the number of points of the signal data.

For example, if my signal data has $ N $ points, and it was taken at a time interval of $ \Delta t $, then:

  • The period of this signal is $$ N*\Delta t $$
  • The frequency interval of the Fourier transform is $$ \Delta f=\frac{1}{N*\Delta t} $$

  • The time range of the signal is $$ \text{from}~~~0~~~\text{to}~~~(N-1)*\Delta t $$

  • The frequency range of the Fourier transform is $$ \text{from}~~~0~~~\text{to}~~~(N-1)*\Delta f $$

We can also see from the definition of the discrete Fourier transform that for any frequency, we can shift it with $ \frac{1}{\Delta t} $ and get the same answer since $$ \mathrm e^{2\pi \mathrm i (f_k+1/\Delta t)t_k}=\mathrm e^{2\pi \mathrm i f_k t_k} $$ which means that frequency $ (N-1)\Delta f $ is the same as $ -\Delta f $. So we can change all the second half of the higher frequencies into the their negative counterpart, so that the frequencies are now symmetric around zero frequency:

$$ \begin{array}{cccccccccc} 0 & \Delta f & 2\Delta f & ... & (\frac{N}{2}-1)\Delta f & \frac{N}{2}\Delta f & (\frac{N}{2}+1)\Delta f & ... & (N-1)\Delta f\\ \downarrow & \downarrow &\downarrow && \downarrow & \downarrow & \downarrow & & \downarrow\\ 0 & \Delta f & 2\Delta f & ... & (\frac{N}{2}-1)\Delta f &-\frac{N}{2}\Delta f &(-\frac{N}{2}+1)\Delta f & ... &-\Delta f& \end{array} $$

If we want to arrange the frequencies from negative to positive, we can simply rotate $ N/2 $ places to the right.

Here's one way to go about it, building on this answer and using the OP's function.

dt = 0.05;
els = Table[Sin[t] Sin[t/40]^2, {t, 0., 40 π, dt}];
n = Length[els];
ssf = RotateRight[Range[-n/2, n/2 - 1]/(n dt), n/2];
fft = Fourier[els, FourierParameters -> {-1, 1}];
ListPlot[Select[Transpose[{ssf, Abs[fft]}], Abs[#[[1]]] < 0.5 &], 
     PlotRange -> All, Filling -> Axis]

transform

The only difference here is selecting the $x$-axis values less than 0.5 Hz (needed because otherwise, the data is hard to see). You can eyeball this data to see that it's about right: the curve els has about 20 periods in 2500 samples. This is a normalized frequency of about 0.016, as seen in the graph. Looking at the ssf vector, the exact frequency is 0.159109, which occurs in location 21 of the ssf vector. Location 21 of the fft vector is the one with the magnitude at about 0.25, the peak value.

Update:

For $n$ an odd number, the frequency arrangement is slightly different:

$$ \begin{array}{ccccccccc} 0 & \Delta f & 2\Delta f & ... & (\frac{N-1}{2})\Delta f & (\frac{N+1}{2})\Delta f & ... & (N-1)\Delta f\\ \downarrow & \downarrow &\downarrow && \downarrow & \downarrow & & \downarrow\\ 0 & \Delta f & 2\Delta f & ... & (\frac{N-1}{2})\Delta f &(-\frac{N-1}{2})\Delta f & ... &-\Delta f& \end{array} $$

so for the data with an odd number of points, we only need to change the frequencies to be

ssf = RotateRight[Range[-(n-1)/2, (n-1)/2 ]/(n dt), (n+1)/2];

and we will be OK.


I am writing this answer to put on record how to do the equivalent of fftshift() in MATLAB, for both the one- and two-dimensional cases.

In Mathematica, the shifting function can be implemented in terms of RotateRight[], like so:

fftshift[dat_?ArrayQ, k : (_Integer?Positive | All) : All] :=
         Module[{dims = Dimensions[dat]}, 
                RotateRight[dat, If[k === All, Quotient[dims, 2], 
                                    Quotient[dims[[k]], 2] UnitVector[Length[dims], k]]]]

The function ifftshift() can be implemented similarly (spot the difference!):

ifftshift[dat_?ArrayQ, k : (_Integer?Positive | All) : All] := 
          Module[{dims = Dimensions[dat]}, 
                 RotateRight[dat, If[k === All, Ceiling[dims/2], 
                                     Ceiling[dims[[k]]/2] UnitVector[Length[dims], k]]]]

Using a slightly modified version of the OP's example, here's how to shift in the one-dimensional case:

h = 1/20;
els = Table[N[Sin[2 π t] Sin[π t/20]^2], {t, 0, 20 - h, h}];

ListPlot[Abs[fftshift[Fourier[els, FourierParameters -> {1, -1}]]]^2/Length[els],
         DataRange -> {-1/(2 h), 1/(2 h)}, Filling -> Axis, PlotRange -> {{-2, 2}, All}]

squared magnitude of shifted FFT

(Note that I am using the square of the magnitude.)


For the two-dimensional case, let's use a classical test image:

cam = Import["https://i.stack.imgur.com/GO2jS.png"]

cameraman

Now, transform and shift:

tst = Abs[fftshift[Fourier[ImageData[cam], FourierParameters -> {1, -1}]]]^2;

Show the (logarithmically transformed) magnitude:

Image[Log[1 + tst]] // ImageAdjust

shifted squared magnitude of 2D FFT

This is effectively what's being done by ImagePeriodogram[]:

ImagePeriodogram[cam]

ImagePeriodogram result


Thanks for a complete answer bill and Ziofil.

Adding further to your answer, in the case of a 2D Fourier transform of an image:

  • Case1: ImagePeriodogram[image] would give you the Fourier transform of the input image right answer, with DC centered in the middle of the resultant image.
  • Case2: scaledPower = Image[PeriodogramArray[image]] would give you the Fourier transform of an image with DC peak not at the center of the image but, at the corners.

For Case2: To bring the DC peak frequency back to the center of the image, simply follow the following commands (assuming your image has even width and height):

width = ImageDimensions[scaledPower][[1]];
height = ImageDimensions[scaledPower][[2]];
widthHalf = width * 0.5;
heightHalf = height * 0.5;
firstQuad =  ImageCrop[scaledPower, {widthHalf, heightHalf}, {Left, Bottom}];
secondQuad = ImageCrop[scaledPower, {widthHalf, heightHalf}, {Right, Bottom}];
thirdQuad =  ImageCrop[scaledPower, {widthHalf, heightHalf}, {Right, Top}];
fourthQuad = ImageCrop[scaledPower, {widthHalf, heightHalf}, {Left, Top}];

ImageCollage[{fourthQuad, thirdQuad, firstQuad, secondQuad}]