Search code examples
opencvimage-processingfft

How to rotate a non-squared image in frequency domain


I want to rotate an image in frequency domain. Inspired in the answers in Image rotation and scaling the frequency domain? I managed to rotate square images. (See the following python script using OpenCV)

M = cv2.imread("lenna.png")
M=np.float32(M)
hanning=cv2.createHanningWindow((M.shape[1],M.shape[0]),cv2.CV_32F)
M=hanning*M
sM = fftshift(M)
rotation_center=(M.shape[1]/2,M.shape[0]/2)
rot_matrix=cv2.getRotationMatrix2D(rotation_center,angle,1.0)

FsM = fftshift(cv2.dft(sM,flags = cv2.DFT_COMPLEX_OUTPUT))
rFsM=cv2.warpAffine(FsM,rot_matrix,(FsM.shape[1],FsM.shape[0]),flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_CONSTANT)
IrFsM = ifftshift(cv2.idft(ifftshift(rFsM),flags=cv2.DFT_REAL_OUTPUT))

This works fine with squared images. (Better results could be achieved by padding the image) Squared image rotation

However, when only using a non-squared portion of the image, the rotation in frequency domain shows some kind of shearing effect. Non-squared image rotation2

Any idea on how to achieve this? Obivously I could pad the image to make it square, however the final purpose of all this is to rotate FFTs as fast as possible for an iterative image registration algorithm and this would slightly slow down the algorithm.


Solution

  • Following the suggestion of @CrisLuengo I found the affine transform needed to avoid padding the image. Obviously it will depend on the image size and the application but for my case avoidding the padding is very interesting. The modified script looks now like:

    #rot_matrix=cv2.getRotationMatrix2D(rotation_center,angle,1.0)
    kx=1.0
    ky=1.0
    if(M.shape[0]>M.shape[1]):
        kx= float(M.shape[0]) / M.shape[1]
    else:
        ky=float(M.shape[1])/M.shape[0]
    
    affine_transform = np.zeros([2, 3])
    affine_transform[0, 0] = np.cos(angle)
    affine_transform[0, 1] = np.sin(angle)*ky/kx
    affine_transform[0, 2] = (1-np.cos(angle))*rotation_center[0]-ky/kx*np.sin(angle)*rotation_center[1]
    affine_transform[1, 0] = -np.sin(angle)*kx/ky
    affine_transform[1, 1] = np.cos(angle)
    affine_transform[1, 2] = kx/ky*np.sin(angle)*rotation_center[0]+(1-np.cos(angle))*rotation_center[1]
    
    FsM = fftshift(cv2.dft(sM,flags = cv2.DFT_COMPLEX_OUTPUT))
    rFsM=cv2.warpAffine(FsM,affine_transform, (FsM.shape[1],FsM.shape[0]),flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_CONSTANT)
    IrFsM = ifftshift(cv2.idft(ifftshift(rFsM),flags=cv2.DFT_REAL_OUTPUT))
    

    Properly rotated in frequency domain