from __future__ import annotations
# skimage.morphology takes very long time to import if <0.19.
import skimage
from skimage import transform as sktrans
from skimage import filters as skfil
from skimage import exposure as skexp
from skimage import measure as skmes
from skimage import segmentation as skseg
from skimage import restoration as skres
from skimage import feature as skfeat
from skimage import registration as skreg
from skimage import graph as skgraph
from skimage import util as skutil
import numpy as np

from functools import reduce, lru_cache

# same as the function in skimage.filters._fft_based (available in scikit-image >= 0.19)
@lru_cache(maxsize=4)
def _get_ND_butterworth_filter(shape: tuple[int, ...], cutoff: float, order: int, 
                               high_pass: bool, real: bool):
    ranges = []
    for d, fc in zip(shape, cutoff):
        axis = np.arange(-(d - 1) // 2, (d - 1) // 2 + 1, dtype=np.float32) / (d*fc)
        ranges.append(np.fft.ifftshift(axis ** 2))
    if real:
        limit = d // 2 + 1
        ranges[-1] = ranges[-1][:limit]
    q2 = reduce(np.add, np.meshgrid(*ranges, indexing="ij", sparse=True))
    wfilt = 1 / (1 + q2**order)
    if high_pass:
        wfilt = 1 - wfilt
    return wfilt