# ringing artifacts using FFT-based gaussian blurring

I'm trying to do an FFT-based gaussian blur on a grayscale image, and it works, however it seems to introduce ringing artifacts to the result when compared to the expected direct filter. What can I do to mitigate this?

In reality I'm using quite a wide gaussian kernel, so I'd rather not use direct convolution for the blurring.

Example:

The left image is a regular blur, the right one is the FFT-based blur. Note the "ringing" especially in the top middle-left part of the right image. Also it seems like the blur is a bit stronger on the right for some reason.

(python) code:

import numpy as np
from scipy import ndimage, misc
import matplotlib.pyplot as plt

ascent = misc.ascent()[300:450, 100:250].astype(np.float64)

input_ = np.fft.rfft2(ascent)
result = ndimage.fourier_gaussian(input_, sigma=1.5)
result = np.fft.irfft2(result)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(5, 2.5))
plt.tight_layout()

plt.gray()
ax1.imshow(ndimage.gaussian_filter(ascent, 1.5, mode='wrap', truncate=10))
ax2.imshow(result)

plt.savefig("fourier gaussian blur test.png")


I apologize if this question is trivial, I don't know much about image processing.

• does the documentation of fourier_gaussian specify the size of the Gaussian kernel? (or, if it doesn't, what does the result look like when your input is a single white pixel against black background)? I suspect edge / windowing effects. Jun 26 '21 at 18:34
• though I must admit, I'm not really seeing ringing – I see a low-pass approximation of the original image, which has strong periodic components due to the vertical bars, so when you apply a low-pass filter, you'll kill the high-frequency components that make this looks like dark, well-defined bars before a light background and are left with the fundamental sines. Jun 26 '21 at 18:42
• @MarcusMüller It looks like this. Very noticeable artifacts but only in one direction Jun 26 '21 at 18:42
• ah excellent! Yeah, this means the thing is assuming wrong FFT type/size in one direction; Cris is right! Jun 26 '21 at 23:08

This means that you should either use the normal fft2, which produces a complex-valued result, or you should set the n argument to the filter function so it knows that the array you pass in is the result of rff2. Note that for rfft the output has a different size than the input (it returns half the array), and that there is an odd-sized input that produces the same size output as any given even-sized input. So with rfft2 it is important to remember the size of the original image. You will have to pass it to irfft2 as well.