Source code for corrct.denoisers

# -*- coding: utf-8 -*-
"""
Advanced denoising methods.

@author: Nicola VIGANĂ’, Computational Imaging group, CWI, The Netherlands,
and ESRF - The European Synchrotron, Grenoble, France
"""

import numpy as np
import scipy.signal as spsig

from . import operators
from . import data_terms
from . import regularizers
from . import solvers
from . import param_tuning

from typing import Optional, Tuple, Union, Callable

from numpy.typing import ArrayLike, NDArray


eps = np.finfo(np.float32).eps


[docs] def denoise_image( img: NDArray, reg_weight: Union[float, ArrayLike, NDArray] = 1e-2, psf: Union[ArrayLike, NDArray, None] = None, pix_weights: Optional[NDArray] = None, iterations: int = 250, regularizer: Callable = lambda rw: regularizers.Regularizer_l1dwl(rw, "bior4.4", 3), lower_limit: Optional[float] = None, verbose: bool = False, ) -> NDArray: """ Denoise an image. Image denoiser based on (flat or weighted) least-squares, with wavelet minimization regularization. The weighted least-squares requires the local pixel-wise weights. It can be used to denoise sinograms and projections. Parameters ---------- img : NDArray The image or sinogram to denoise. reg_weight : Union[float, ArrayLike, NDArray], optional Weight of the regularization term. The default is 1e-2. If a sequence / array is passed, all the different values will be tested. The one minimizing the error over the cross-validation set will be chosen and returned. pix_weights : Union[ArrayLike, NDArray, None], optional The local weights of the pixels, for a weighted least-squares minimization. If None, a standard least-squares minimization is performed. The default is None. iterations : int, optional Number of iterations. The default is 250. regularizer : Callable, optional The one-argument constructor of a regularizer. The default is the DWL regularizer. lower_limit : Optional[float], optional Lower clipping limit of the image. The default is None. verbose : bool, optional Turn verbosity on. The default is False. Returns ------- NDArray Denoised image or sinogram. """ if psf is None: op = operators.TransformIdentity(img.shape) else: op = operators.TransformConvolution(img.shape, psf) if pix_weights is None: data_term = data_terms.DataFidelity_l2() else: data_term = data_terms.DataFidelity_wl2(pix_weights) def solver_spawn(lam_reg): # Using the PDHG solver from Chambolle and Pock reg = regularizer(lam_reg) return solvers.PDHG(verbose=verbose, data_term=data_term, regularizer=reg, data_term_test=data_term) def solver_call(solver: solvers.Solver, b_test_mask: Optional[NDArray] = None) -> Tuple[NDArray, solvers.SolutionInfo]: x0 = img.copy() if b_test_mask is not None: med_img = spsig.medfilt2d(img, kernel_size=11) masked_pixels = b_test_mask > 0.5 x0[masked_pixels] = med_img[masked_pixels] return solver(op, img, iterations, x0=x0, lower_limit=lower_limit, b_test_mask=b_test_mask) reg_weight = np.array(reg_weight) if reg_weight.size > 1: reg_help_cv = param_tuning.CrossValidation(img.shape, verbose=True, num_averages=5, plot_result=False) reg_help_cv.solver_spawning_function = solver_spawn reg_help_cv.solver_calling_function = solver_call f_avgs, f_stds, _ = reg_help_cv.compute_loss_values(reg_weight) reg_help_cv.plot_result = True reg_weight, _ = reg_help_cv.fit_loss_min(reg_weight, f_avgs, f_stds=f_stds) solver = solver_spawn(reg_weight) (denoised_img, _) = solver_call(solver, None) return denoised_img