Include padding in FourierFilters

Currently, FourierFilter from hotopy.image._filter applies a filter in FFT space (surprise), which assumes periodic boundary conditions. A common use case for GaussianBlur and GaussianBandpass would require padding to reduce boundary artifacts like

from hotopy.utils import Padder
from hotopy.image.filter import GaussianBlur
import numpy as np

data = np.random.random((128, 128)) + np.linspace(0, 1, 128)[None,:]
sigma = 3

npad = 2 * sigma  # npad = 0 produces edge artifacts
padder = Padder(npad, mode="edge")
padded_shape = tuple(s + 2 * npad for s in data.shape)
gaussfilt = GaussianBlur(padded_shape, sigma)
filtered_data = padder.inv(gaussfilt(padder(data)))

I would prefer the option to include padding in FourierFilter already. Something like:

from hotopy.image.filter import GaussianBlur
import numpy as np

data = np.random.random((128, 128)) + np.linspace(0, 1, 128)[None,:]
sigma = 3

gaussfilt = GaussianBlur(data.shape, sigma, npad=2 * sigma)
filtered_data = gaussfilt(data)

It would make sense to let FourierFilter.__init__ accept npad or maybe even a Padder instance and the inheriting classes calling super().__init__.

Maybe there is even some smart and fast FFT implicit padding way to do the padding, would need mode="edge" padding in dataspace though... for now that sounds like premature optimization to me.

Edited by Paul Meyer
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information