diff --git a/qim3d/filters/_common_filter_methods.py b/qim3d/filters/_common_filter_methods.py index bc4b02f0de5e94eb3e41a17786a04b950a36696d..87d6b6096d1b119e1d384f84b7f9fb08ffcf8198 100644 --- a/qim3d/filters/_common_filter_methods.py +++ b/qim3d/filters/_common_filter_methods.py @@ -27,7 +27,11 @@ __all__ = [ class FilterBase: - def __init__(self, *args, dask: bool = False, chunks: str = "auto", **kwargs): + def __init__(self, + *args, + dask: bool = False, + chunks: str = "auto", + **kwargs): """ Base class for image filters. @@ -42,6 +46,18 @@ class FilterBase: class Gaussian(FilterBase): + def __init__(self, sigma: float, *args, **kwargs): + """ + Gaussian filter initialization. + + Args: + sigma (float): Standard deviation for Gaussian kernel. + *args: Additional arguments. + **kwargs: Additional keyword arguments. + """ + super().__init__(*args, **kwargs) + self.sigma = sigma + def __call__(self, input: np.ndarray) -> np.ndarray: """ Applies a Gaussian filter to the input. @@ -53,11 +69,27 @@ class Gaussian(FilterBase): The filtered image or volume. """ return gaussian( - input, dask=self.dask, chunks=self.chunks, *self.args, **self.kwargs + input, sigma=self.sigma, dask=self.dask, chunks=self.chunks, *self.args, **self.kwargs ) class Median(FilterBase): + def __init__(self, size: float = None, footprint: np.ndarray = None, *args, **kwargs): + """ + Median filter initialization. + + Args: + size (float or tuple, optional): Filter size. + footprint (np.ndarray, optional): The structuring element for filtering. + *args: Additional arguments. + **kwargs: Additional keyword arguments. + """ + if size is None and footprint is None: + raise ValueError("Either 'size' or 'footprint' must be provided.") + super().__init__(*args, **kwargs) + self.size = size + self.footprint = footprint + def __call__(self, input: np.ndarray) -> np.ndarray: """ Applies a median filter to the input. @@ -68,10 +100,26 @@ class Median(FilterBase): Returns: The filtered image or volume. """ - return median(input, dask=self.dask, chunks=self.chunks, **self.kwargs) + return median(vol=input, size=self.size, footprint=self.footprint, dask=self.dask, chunks=self.chunks, **self.kwargs) class Maximum(FilterBase): + def __init__(self, size: float = None, footprint: np.ndarray = None, *args, **kwargs): + """ + Maximum filter initialization. + + Args: + size (float or tuple, optional): Filter size. + footprint (np.ndarray, optional): The structuring element for filtering. + *args: Additional arguments. + **kwargs: Additional keyword arguments. + """ + if size is None and footprint is None: + raise ValueError("Either 'size' or 'footprint' must be provided.") + super().__init__(*args, **kwargs) + self.size = size + self.footprint = footprint + def __call__(self, input: np.ndarray) -> np.ndarray: """ Applies a maximum filter to the input. @@ -82,10 +130,26 @@ class Maximum(FilterBase): Returns: The filtered image or volume. """ - return maximum(input, dask=self.dask, chunks=self.chunks, **self.kwargs) + return maximum(vol=input, size=self.size, footprint=self.footprint, dask=self.dask, chunks=self.chunks, **self.kwargs) class Minimum(FilterBase): + def __init__(self, size: float = None, footprint: np.ndarray = None, *args, **kwargs): + """ + Minimum filter initialization. + + Args: + size (float or tuple, optional): Filter size. + footprint (np.ndarray, optional): The structuring element for filtering. + *args: Additional arguments. + **kwargs: Additional keyword arguments. + """ + if size is None and footprint is None: + raise ValueError("Either 'size' or 'footprint' must be provided.") + super().__init__(*args, **kwargs) + self.size = size + self.footprint = footprint + def __call__(self, input: np.ndarray) -> np.ndarray: """ Applies a minimum filter to the input. @@ -96,7 +160,7 @@ class Minimum(FilterBase): Returns: The filtered image or volume. """ - return minimum(input, dask=self.dask, chunks=self.chunks, **self.kwargs) + return minimum(vol=input, size=self.size, footprint=self.footprint, dask=self.dask, chunks=self.chunks, **self.kwargs) class Tophat(FilterBase): @@ -217,13 +281,18 @@ class Pipeline: def gaussian( - vol: np.ndarray, dask: bool = False, chunks: str = "auto", *args, **kwargs -) -> np.ndarray: + vol: np.ndarray, + sigma: float, + dask: bool = False, + chunks: str = "auto", + **kwargs + ) -> np.ndarray: """ Applies a Gaussian filter to the input volume using scipy.ndimage.gaussian_filter or dask_image.ndfilters.gaussian_filter. Args: vol (np.ndarray): The input image or volume. + sigma (float or sequence of floats): The standard deviations of the Gaussian filter are given for each axis as a sequence, or as a single number, in which case it is equal for all axes. dask (bool, optional): Whether to use Dask for the Gaussian filter. chunks (int or tuple or "'auto'", optional): Defines how to divide the array into blocks when using Dask. Can be an integer, tuple, size in bytes, or "auto" for automatic sizing. *args (Any): Additional positional arguments for the Gaussian filter. @@ -236,92 +305,137 @@ def gaussian( if dask: if not isinstance(vol, da.Array): vol = da.from_array(vol, chunks=chunks) - dask_vol = dask_ndfilters.gaussian_filter(vol, *args, **kwargs) + dask_vol = dask_ndfilters.gaussian_filter(vol, sigma, **kwargs) res = dask_vol.compute() return res else: - res = ndimage.gaussian_filter(vol, *args, **kwargs) + res = ndimage.gaussian_filter(vol, sigma, **kwargs) return res def median( - vol: np.ndarray, dask: bool = False, chunks: str = "auto", **kwargs + vol: np.ndarray, + size: float = None, + footprint: np.ndarray = None, + dask: bool = False, + chunks: str = "auto", + **kwargs ) -> np.ndarray: """ Applies a median filter to the input volume using scipy.ndimage.median_filter or dask_image.ndfilters.median_filter. Args: vol (np.ndarray): The input image or volume. + size (scalar or tuple, optional): Either size or footprint must be defined. size gives the shape that is taken from the input array, at every element position, to define the input to the filter function. + footprint (np.ndarray, optional): Boolean array that specifies (implicitly) a shape, but also which of the elements within this shape will get passed to the filter function. dask (bool, optional): Whether to use Dask for the median filter. chunks (int or tuple or "'auto'", optional): Defines how to divide the array into blocks when using Dask. Can be an integer, tuple, size in bytes, or "auto" for automatic sizing. **kwargs (Any): Additional keyword arguments for the median filter. Returns: filtered_vol (np.ndarray): The filtered image or volume. + + Raises: + RuntimeError: If neither size nor footprint is defined """ + if size is None: + if footprint is None: + raise RuntimeError("no footprint or filter size provided") + if dask: if not isinstance(vol, da.Array): vol = da.from_array(vol, chunks=chunks) - dask_vol = dask_ndfilters.median_filter(vol, **kwargs) + dask_vol = dask_ndfilters.median_filter(vol, size, footprint, **kwargs) res = dask_vol.compute() return res else: - res = ndimage.median_filter(vol, **kwargs) + res = ndimage.median_filter(vol, size, footprint, **kwargs) return res def maximum( - vol: np.ndarray, dask: bool = False, chunks: str = "auto", **kwargs + vol: np.ndarray, + size: float = None, + footprint: np.ndarray = None, + dask: bool = False, + chunks: str = "auto", + **kwargs ) -> np.ndarray: """ Applies a maximum filter to the input volume using scipy.ndimage.maximum_filter or dask_image.ndfilters.maximum_filter. Args: vol (np.ndarray): The input image or volume. + size (scalar or tuple, optional): Either size or footprint must be defined. size gives the shape that is taken from the input array, at every element position, to define the input to the filter function. + footprint (np.ndarray, optional): Boolean array that specifies (implicitly) a shape, but also which of the elements within this shape will get passed to the filter function. dask (bool, optional): Whether to use Dask for the maximum filter. chunks (int or tuple or "'auto'", optional): Defines how to divide the array into blocks when using Dask. Can be an integer, tuple, size in bytes, or "auto" for automatic sizing. **kwargs (Any): Additional keyword arguments for the maximum filter. Returns: filtered_vol (np.ndarray): The filtered image or volume. + + Raises: + RuntimeError: If neither size nor footprint is defined """ + if size is None: + if footprint is None: + raise RuntimeError("no footprint or filter size provided") + if dask: if not isinstance(vol, da.Array): vol = da.from_array(vol, chunks=chunks) - dask_vol = dask_ndfilters.maximum_filter(vol, **kwargs) + dask_vol = dask_ndfilters.maximum_filter(vol, size, footprint, **kwargs) res = dask_vol.compute() return res else: - res = ndimage.maximum_filter(vol, **kwargs) + res = ndimage.maximum_filter(vol, size, footprint, **kwargs) return res def minimum( - vol: np.ndarray, dask: bool = False, chunks: str = "auto", **kwargs + vol: np.ndarray, + size: float = None, + footprint: np.ndarray = None, + dask: bool = False, + chunks: str = "auto", + **kwargs ) -> np.ndarray: """ Applies a minimum filter to the input volume using scipy.ndimage.minimum_filter or dask_image.ndfilters.minimum_filter. Args: vol (np.ndarray): The input image or volume. + size (scalar or tuple, optional): Either size or footprint must be defined. size gives the shape that is taken from the input array, at every element position, to define the input to the filter function. + footprint (np.ndarray, optional): Boolean array that specifies (implicitly) a shape, but also which of the elements within this shape will get passed to the filter function. dask (bool, optional): Whether to use Dask for the minimum filter. chunks (int or tuple or "'auto'", optional): Defines how to divide the array into blocks when using Dask. Can be an integer, tuple, size in bytes, or "auto" for automatic sizing. **kwargs (Any): Additional keyword arguments for the minimum filter. Returns: filtered_vol (np.ndarray): The filtered image or volume. + + Raises: + RuntimeError: If neither size nor footprint is defined """ + if size is None: + if footprint is None: + raise RuntimeError("no footprint or filter size provided") + if dask: if not isinstance(vol, da.Array): vol = da.from_array(vol, chunks=chunks) - dask_vol = dask_ndfilters.minimum_filter(vol, **kwargs) + dask_vol = dask_ndfilters.minimum_filter(vol, size, footprint, **kwargs) res = dask_vol.compute() return res else: - res = ndimage.minimum_filter(vol, **kwargs) + res = ndimage.minimum_filter(vol, size, footprint, **kwargs) return res -def tophat(vol, dask=False, **kwargs): +def tophat(vol: np.ndarray, + dask: bool = False, + **kwargs + ): """ Remove background from the volume.