# Is rotation of a Fourier transform the same thing as Fourier transform of a rotation?

I'm working on an image processing problem and wondering if DFT(rotation(image)) == rotation(DFT(image)) (1). My final goal is to apply rotations in the Fourier domain then do an inverse Fourier transform and get the rotated image.

This question is pretty close to what I'm looking for. That question links to ressources saying that (1) is true in theory (for infinite signals and infinite frequency bins), and the accepted answer shows that it is so mathematically. However, the answers also point out that it may be actually hard to reproduce in the "real world" with DFT algorithms, because of artefacts.

I've tried to reproduce (1) but have so far failed to do so, and I'm under the impression that something is wronger than just "artefacts". My experiments can be summed up in the following image :

The images I'm showing here in the Fourier domain are zooms of the imaginary parts of the FTs, with normalized intensity to enhance visualization. However, the point stands for the real part, and also for magnitude and phase spectrums if you choose to visualize them like so.
As you can see, I am not getting results consistent with (1), as the two images of the last column are not equal (at all). And I feel like the problem is in the rotation of the Fourier image. The rotated Fourier image looks nothing like the Fourier of the rotated. I actually don't really get how the latter is computed: even with a 45 degrees rotation, the middle part stays about the same for instance (look at the 4 black neighbours of the central white pixels, they are the same in both images of column 2).

To emphasize my point, I made an animation showing (left) the FT of the original image, (middle) the rotation of the Fourier, (right) the Fourier of the rotation, for angles varying between 0 and 90

I've tried more severe padding of the original image, am careful with bit depths, have tried rotation magnitude and phase instead of real and imaginary parts, with no luck so far. Is there something I'm doing very wrong ? How can I achieve my goal to "correctly" apply rotation in the Fourier domain ?

Here is the code to reproduce the bulk of the experiments (in Python, using OpenCV and Scipy) as well as the original image

import numpy as np
import cv2
from scipy import ndimage

# Helper function to save an image with a normalized range for visualization
def normalize_and_cvt_to_uint8(im):
min_ = np.min(im)
max_ = np.max(im)
im_norm = (im - min_) / (max_ - min_) * 255
return im_norm.astype(np.uint8)

# Rotate a complex image
def rot(im_tot, angle):
# Get real and imaginary part
im_real = im_tot[:,:,0]
im_imag = im_tot[:,:,1]

# Rotate each part individually
im_real_rot = ndimage.rotate(im_real, angle, reshape = False)
im_imag_rot = ndimage.rotate(im_imag, angle, reshape = False)

# Recreate complex image
im_rot = np.dstack((im_real_rot, im_imag_rot))

return im_rot

# Helper function to pad an image to a given shape
[h, w] = im.shape[:2]
delta_w = goal_w - w
delta_h = goal_h - h
top, bottom = delta_h // 2, delta_h - (delta_h // 2)
left, right = delta_w // 2, delta_w - (delta_w // 2)
return cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT, value=0)

path_im = "poivrons.png"
im_shape = 801
cv2.imwrite("starting_im.png", im)

# Compute DFT of original image
dft1 = cv2.dft(np.float32(im), flags=cv2.DFT_COMPLEX_OUTPUT)
dft1 = np.fft.fftshift(dft1)
cv2.imwrite("dft1.png", normalize_and_cvt_to_uint8(dft1[:,:,1]))

# Rotate original image
angle = 45
im_rot = ndimage.rotate(im, angle, reshape=False)
cv2.imwrite("rotated_img.png", im_rot)

# Compute DFT of rotated image
dft2 = cv2.dft(np.float32(im_rot), flags=cv2.DFT_COMPLEX_OUTPUT)
dft2 = np.fft.fftshift(dft2)
cv2.imwrite("dft2.png", normalize_and_cvt_to_uint8(dft2[:,:,1]))

# Rotate DFT of original image
dft3 = rot(dft1, angle)
dft3 = np.fft.fftshift(dft3)

# Compute IDFT of rotated DFT
cv2.imwrite("dft3.png", normalize_and_cvt_to_uint8(dft3[:,:,1]))
im_back = cv2.idft(np.fft.ifftshift(dft3), flags=cv2.DFT_SCALE | cv2.DFT_REAL_OUTPUT)
cv2.imwrite("back_img.png", im_back)

• I haven’t look at your code in detail, but make sure your rotation in the frequency domain is an actual rotation. Look at your first image. The FFT of the rotated image looks like a rotated version of the FFT of the original image. However, after you rotate the FFT the result looks incorrect. May 19, 2020 at 19:27
• Is the checkerboard visible in the Fourier transform one sample each "checker"? If so, whatever interpolation you're doing to rotate the thing may just be smearing it out too much. May 19, 2020 at 22:56
• I tried this with diagonal lines (by setting the "image" to the identity matrix) and got roughly the same results. I suspect you're running into problems with the rotation because the rotated bins don't land on the grid. Starting with a bigger image and windowing (in the 1D sense) with a Hanning or other traditional DSP-ish window may help. May 20, 2020 at 0:19
• @user110971 It's true that it may not look that way, but the situation is actually the opposite of what you're saying. The Fourier of the rotated doesn't look like a rotation but it's the true rotation of the Fourier, where the checkered pattern "blends" together. In particular, in the image where rotation angle is 45 degrees, the checkered lines are roughly aligned with the image edges, causing the pattern to dissapear. Conversly, the Fourier of the rotated (which you call more realistic) is not a rotation at all. See for example the 8 middle pixels of the images in the second column : May 20, 2020 at 8:13
• @user110971 they haven't changed at all while the image went under a "45 degrees rotation" (ie Fourier is applied to a 45 degrees rotated image) May 20, 2020 at 8:14