Search code examples
pythonnumpyopencvimage-processingfft

Reproducing the phase spectrum while using np.fft.fft2 and cv2.dft. Why are the results not similar?


Another question was asking about the correct way of getting magnitude and phase spectra while using cv2.dft.

My answer was limited to the numpy approach and then I thought that using OpenCV for this would be even nicer. I am currently trying to reproduce the same results but I am seeing significant differences in the phase spectrum.

Here are my imports:

%matplotlib notebook
import matplotlib.pyplot as plt
import numpy as np
import cv2
im = np.zeros((50, 50), dtype = np.float32) # create empty array
im[2:10, 2:10] = 255 # draw a rectangle

The numpy example and results:


imFFTNumpy = np.fft.fft2(im)
imFFTNumpyShifted = np.fft.fftshift(imFFTNumpy)
magSpectrumNumpy = np.abs(imFFTNumpyShifted)
phaseSpectrumNumpy = np.angle(imFFTNumpyShifted)
fig, ax = plt.subplots(nrows = 1, ncols = 3)
ax[0].imshow(im)
ax[1].imshow(magSpectrumNumpy)
ax[2].imshow(phaseSpectrumNumpy)
plt.suptitle("Using Numpy np.fft.fft2 and np.abs/ np.angle")

Numpy results

The OpenCV example and results:

imFFTOpenCV = cv2.dft(im, flags=cv2.DFT_COMPLEX_OUTPUT)
imFFTOpenCVShifted = np.fft.fftshift(imFFTOpenCV)
magSpectrumOpenCV, phaseSpectrumOpenCV = cv2.cartToPolar(imFFTOpenCVShifted[:,:,0], imFFTOpenCVShifted[:,:,1])
fig, ax = plt.subplots(nrows = 1, ncols = 3)
ax[0].imshow(im)
ax[1].imshow(magSpectrumOpenCV)
ax[2].imshow(phaseSpectrumOpenCV)
plt.suptitle("Using OpenCV cv2.dft and cv2.cartToPolar")

OpenCV results

As you can see, while the magnitude spectrum looks the same (it has some expected deviations due to floating-point arithmetic), the phase spectrum looks significantly different. I dug around a bit and found out that OpenCV usually returns phase from 0 to 2π, whereas np.angle returns the phase from -π to +π. Subtracting π from the OpenCV phase does not correct difference though.

What could be the reason for this? Is it possible to get almost identical phase using both approaches, just like with magnitude?


Solution

  • Given that imFFTOpenCV is a 3D array because OpenCV doesn’t understand complex numbers, np.fft.fftshift(imFFTOpenCV) will swap the real and complex planes. That is, the shift happens in all 3 dimensions of the array.

    So when computing the phase and magnitude, you need to take this swap into account:

    magSpectrumOpenCV, phaseSpectrumOpenCV = cv2.cartToPolar(imFFTOpenCVShifted[:,:,1], imFFTOpenCVShifted[:,:,0])
    

    Alternatively, and this is probably more readable, you could tell NumPy which axes you want shifted:

    imFFTOpenCVShifted = np.fft.fftshift(imFFTOpenCV, axes=(0, 1))