Source code for aydin.it.classic_denoisers.bilateral

from functools import partial
from typing import Optional, List, Tuple

import numpy
from numpy.typing import ArrayLike
from skimage.restoration import denoise_bilateral as skimage_denoise_bilateral

from aydin.it.classic_denoisers import _defaults
from aydin.util.crop.rep_crop import representative_crop
from aydin.util.denoise_nd.denoise_nd import extend_nd
from aydin.util.j_invariance.j_invariance import calibrate_denoiser


def calibrate_denoise_bilateral(
    image: ArrayLike,
    bins: int = 10000,
    crop_size_in_voxels: Optional[int] = _defaults.default_crop_size_normal.value,
    optimiser: str = _defaults.default_optimiser.value,
    max_num_evaluations: int = _defaults.default_max_evals_normal.value,
    blind_spots: Optional[List[Tuple[int]]] = _defaults.default_blind_spots.value,
    jinv_interpolation_mode: str = _defaults.default_jinv_interpolation_mode.value,
    display_images: bool = False,
    display_crop: bool = False,
    **other_fixed_parameters,
):
    """
    Calibrates the bilateral denoiser for the given image and returns the optimal
    parameters obtained using the N2S loss.

    Note: it seems that the bilateral filter of scikit-image
    is broken!

    Parameters
    ----------
    image: ArrayLike
        Image to calibrate denoiser for.

    bins: int
        Number of discrete values for Gaussian weights of
        color filtering. A larger value results in improved
        accuracy.
        (advanced)

    crop_size_in_voxels: int or None for default
        Number of voxels for crop used to calibrate denoiser.
        Increase this number by factors of two if denoising quality is
        unsatisfactory -- this can be important for very noisy images.
        Values to try are: 65000, 128000, 256000, 320000.
        We do not recommend values higher than 512000.

    optimiser: str
        Optimiser to use for finding the best denoising
        parameters. Can be: 'smart' (default), or 'fast' for a mix of SHGO
        followed by L-BFGS-B.
        (advanced)

    max_num_evaluations: int
        Maximum number of evaluations for finding the optimal parameters.
        Increase this number by factors of two if denoising quality is
        unsatisfactory.

    blind_spots: bool
        List of voxel coordinates (relative to receptive field center) to
        be included in the blind-spot. For example, you can give a list of
        3 tuples: [(0,0,0), (0,1,0), (0,-1,0)] to extend the blind spot
        to cover voxels of relative coordinates: (0,0,0),(0,1,0), and (0,-1,0)
        (advanced) (hidden)

    jinv_interpolation_mode: str
        J-invariance interpolation mode for masking. Can be: 'median' or
        'gaussian'.
        (advanced)

    display_images: bool
        When True the denoised images encountered during
        optimisation are shown
        (advanced) (hidden)

    display_crop: bool
        Displays crop, for debugging purposes...
        (advanced) (hidden)

    other_fixed_parameters: dict
        Any other fixed parameters

    Returns
    -------
    Denoising function, dictionary containing optimal parameters,
    and free memory needed in bytes for computation.
    """
    # Convert image to float if needed:
    image = image.astype(dtype=numpy.float32, copy=False)

    # obtain representative crop, to speed things up...
    crop = representative_crop(
        image, crop_size=crop_size_in_voxels, display_crop=display_crop
    )

    # Parameters to test when calibrating the denoising algorithm
    parameter_ranges = {'sigma_spatial': (0.01, 1), 'sigma_color': (0.01, 1)}

    # Combine fixed parameters:
    other_fixed_parameters = other_fixed_parameters | {'bins': bins}

    # Partial function:
    _denoise_bilateral = partial(denoise_bilateral, **other_fixed_parameters)

    # Calibrate denoiser
    best_parameters = (
        calibrate_denoiser(
            crop,
            _denoise_bilateral,
            mode=optimiser,
            denoise_parameters=parameter_ranges,
            interpolation_mode=jinv_interpolation_mode,
            max_num_evaluations=max_num_evaluations,
            blind_spots=blind_spots,
            display_images=display_images,
        )
        | other_fixed_parameters
    )

    # Memory needed:
    memory_needed = 2 * image.nbytes

    return denoise_bilateral, best_parameters, memory_needed


[docs]def denoise_bilateral( image: ArrayLike, sigma_color: Optional[float] = None, sigma_spatial: float = 1, bins: int = 10000, **kwargs, ): """ Denoises the given image using a <a href="https://en.wikipedia.org/wiki/Bilateral_filter">bilateral filter</a>. The bilateral filter is a edge-preserving smoothing filter that can be used for image denoising. Each pixel value is replaced by a weighted average of intensity values from nearby pixels. The weighting is inversely related to the pixel distance in space but also in the pixels value differences. Parameters ---------- image : ArrayLike Image to denoise sigma_color : float Standard deviation for grayvalue/color distance (radiometric similarity). A larger value results in averaging of pixels with larger radiometric differences. Note, that the image will be converted using the `img_as_float` function and thus the standard deviation is in respect to the range ``[0, 1]``. If the value is ``None`` the standard deviation of the ``image`` will be used. sigma_spatial : float Standard deviation for range distance. A larger value results in averaging of pixels with larger spatial differences. bins : int Number of discrete values for Gaussian weights of color filtering. A larger value results in improved accuracy. kwargs: dict Other parameters Returns ------- Denoised image """ # Convert image to float if needed: image = image.astype(dtype=numpy.float32, copy=False) _skimage_denoise_bilateral = extend_nd(available_dims=[2])( skimage_denoise_bilateral ) return _skimage_denoise_bilateral( image, sigma_color=sigma_color, sigma_spatial=sigma_spatial, bins=bins, mode='reflect', **kwargs, )