Skip to content

denoiser

Base class and functions for all denoising algorithms.

@author: Nicola VIGANÒ, CEA-MEM, Grenoble, France

Classes:

Functions:

DataScaleBias dataclass

DataScaleBias(
    scale_inp: float | NDArray = 1.0,
    scale_out: float | NDArray = 1.0,
    scale_tgt: float | NDArray = 1.0,
    bias_inp: float | NDArray = 0.0,
    bias_out: float | NDArray = 0.0,
    bias_tgt: float | NDArray = 0.0,
)

Data scale and bias.

Denoiser

Denoiser(
    model: int | str | NetworkParams | Module | Mapping,
    data_scale_bias: DataScaleBias | None = None,
    reg_val: float | LossRegularizer | None = None,
    device: str = "cuda" if is_available() else "cpu",
    batch_size: int | None = None,
    augmentation: (
        str
        | Augmentation
        | Sequence[str | Augmentation]
        | None
    ) = None,
    save_epochs_dir: str | None = None,
    verbose: bool = True,
)

Bases: ABC

Base denoising class.

Parameters:

  • model (str | NetworkParams | Module | Mapping | None) –

    Type of neural network to use or a specific network (or state) to use

  • data_scale_bias (DataScaleBias | None, default: None ) –

    Scale and bias of the input data, by default None

  • reg_val (float | None, default: None ) –

    Regularization value, by default 1e-5

  • device (str, default: 'cuda' if is_available() else 'cpu' ) –

    Device to use, by default "cuda" if cuda is available, otherwise "cpu"

  • save_epochs_dir (str | None, default: None ) –

    Directory where to save network states at each epoch. If None disabled, by default None

  • verbose (bool, default: True ) –

    Whether to produce verbose output, by default True

Methods:

  • infer

    Inference, given an initial stack of images.

  • train

    Training of the model, given the required input.

Attributes:

  • n_channels_in (int) –

    Returns the number of input channels of the model.

  • n_channels_out (int) –

    Returns the number of output channels of the model.

  • n_dims (int) –

    Returns the expected signal dimensions.

Source code in src/autoden/algorithms/denoiser.py
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
def __init__(
    self,
    model: int | str | NetworkParams | pt.nn.Module | Mapping,
    data_scale_bias: DataScaleBias | None = None,
    reg_val: float | LossRegularizer | None = None,
    device: str = "cuda" if pt.cuda.is_available() else "cpu",
    batch_size: int | None = None,
    augmentation: str | Augmentation | Sequence[str | Augmentation] | None = None,
    save_epochs_dir: str | None = None,
    verbose: bool = True,
) -> None:
    """Initialize the noise2noise method.

    Parameters
    ----------
    model : str | NetworkParams | pt.nn.Module | Mapping | None
        Type of neural network to use or a specific network (or state) to use
    data_scale_bias : DataScaleBias | None, optional
        Scale and bias of the input data, by default None
    reg_val : float | None, optional
        Regularization value, by default 1e-5
    device : str, optional
        Device to use, by default "cuda" if cuda is available, otherwise "cpu"
    save_epochs_dir : str | None, optional
        Directory where to save network states at each epoch.
        If None disabled, by default None
    verbose : bool, optional
        Whether to produce verbose output, by default True
    """
    if isinstance(model, int):
        if self.save_epochs_dir is None:
            raise ValueError("Directory for saving epochs not specified")

        model = load_model_state(self.save_epochs_dir, epoch_num=model)

    if isinstance(model, (str, NetworkParams, Mapping, pt.nn.Module)):
        self.model = create_network(model, device=device)
    else:
        raise ValueError(f"Invalid model {type(model)}")
    if verbose:
        get_num_parameters(self.model, verbose=True)

    # if augmentation is None:
    #     augmentation = []
    # elif isinstance(augmentation, str):
    #     augmentation = [augmentation.lower()]
    # elif isinstance(augmentation, Sequence):
    #     augmentation = [str(a).lower() for a in augmentation]

    self.data_sb = data_scale_bias

    self.reg_val = reg_val
    self.device = device
    self.batch_size = batch_size
    self.augmentation = augmentation
    self.save_epochs_dir = save_epochs_dir
    self.verbose = verbose

n_channels_in property

n_channels_in: int

Returns the number of input channels of the model.

If the model is an instance of SerializableModel and has an init_params attribute containing the key "n_channels_in", this property returns its value. Otherwise, it defaults to 1.

Returns:

  • int

    The number of input channels.

n_channels_out property

n_channels_out: int

Returns the number of output channels of the model.

If the model is an instance of SerializableModel and has an init_params attribute containing the key "n_channels_out", this property returns its value. Otherwise, it defaults to 1.

Returns:

  • int

    The number of output channels.

n_dims property

n_dims: int

Returns the expected signal dimensions.

If the model is an instance of SerializableModel and has an init_params attribute containing the key "n_dims", this property returns its value. Otherwise, it defaults to 2.

Returns:

  • int

    The expected signal dimensions.

infer

infer(
    inp: NDArray, channel_axis_dst: int | None = None
) -> NDArray

Inference, given an initial stack of images.

Parameters:

  • inp (NDArray) –

    The input stack of images

  • channel_axis_dst (int | None, default: None ) –

    The desired channel axis for the output. If None, the output will have the same channel axis as the input.

Returns:

  • NDArray

    The denoised stack of images

Source code in src/autoden/algorithms/denoiser.py
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
def infer(self, inp: NDArray, channel_axis_dst: int | None = None) -> NDArray:
    """Inference, given an initial stack of images.

    Parameters
    ----------
    inp : NDArray
        The input stack of images
    channel_axis_dst : int | None, optional
        The desired channel axis for the output. If None, the output will have the same channel axis as the input.

    Returns
    -------
    NDArray
        The denoised stack of images
    """
    # Rescale input
    if self.data_sb is not None:
        inp = inp * self.data_sb.scale_inp - self.data_sb.bias_inp

    channel_ax_inp = -self.n_dims - 1 if self.n_channels_in > 1 else None
    inp_t = data_to_tensor(inp, device=self.device, n_dims=self.n_dims, channel_axis=channel_ax_inp)

    self.model.eval()
    with pt.inference_mode():
        out_t: pt.Tensor = self.model(inp_t)
        output = out_t.squeeze(dim=(0, 1)).to("cpu").numpy()

    # Rescale output
    if self.data_sb is not None:
        output = (output + self.data_sb.bias_out) / self.data_sb.scale_out

    if channel_axis_dst is not None:
        output = self._move_output_channel_axis(output, channel_axis_dst)

    return output

train abstractmethod

train(*args: Any, **kwds: Any) -> dict[str, NDArray]

Training of the model, given the required input.

Source code in src/autoden/algorithms/denoiser.py
435
436
437
@abstractmethod
def train(self, *args: Any, **kwds: Any) -> dict[str, NDArray]:
    """Training of the model, given the required input."""

compute_scaling_selfsupervised

compute_scaling_selfsupervised(
    inp: NDArray,
) -> DataScaleBias

Compute input data scaling and bias for self-supervised learning.

Parameters:

  • inp (NDArray) –

    Input data.

Returns:

  • DataScaleBias

    An instance of DataScaleBias containing the computed scaling and bias values.

Source code in src/autoden/algorithms/denoiser.py
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
def compute_scaling_selfsupervised(inp: NDArray) -> DataScaleBias:
    """
    Compute input data scaling and bias for self-supervised learning.

    Parameters
    ----------
    inp : NDArray
        Input data.

    Returns
    -------
    DataScaleBias
        An instance of DataScaleBias containing the computed scaling and bias values.
    """
    range_vals_inp = get_normalization_range(inp, percentile=0.001)

    sb = DataScaleBias()
    sb.scale_inp = 1 / (range_vals_inp[1] - range_vals_inp[0])
    sb.scale_out = sb.scale_tgt = sb.scale_inp

    sb.bias_inp = range_vals_inp[2] * sb.scale_inp
    sb.bias_out = sb.bias_tgt = sb.bias_inp

    return sb

compute_scaling_supervised

compute_scaling_supervised(
    inp: NDArray, tgt: NDArray
) -> DataScaleBias

Compute input and target data scaling and bias for supervised learning.

Parameters:

  • inp (NDArray) –

    Input data.

  • tgt (NDArray) –

    Target data.

Returns:

  • DataScaleBias

    An instance of DataScaleBias containing the computed scaling and bias values.

Source code in src/autoden/algorithms/denoiser.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
def compute_scaling_supervised(inp: NDArray, tgt: NDArray) -> DataScaleBias:
    """
    Compute input and target data scaling and bias for supervised learning.

    Parameters
    ----------
    inp : NDArray
        Input data.
    tgt : NDArray
        Target data.

    Returns
    -------
    DataScaleBias
        An instance of DataScaleBias containing the computed scaling and bias values.
    """
    range_vals_inp = get_normalization_range(inp, percentile=0.001)
    range_vals_tgt = get_normalization_range(tgt, percentile=0.001)

    sb = DataScaleBias()
    sb.scale_inp = 1 / (range_vals_inp[1] - range_vals_inp[0])
    sb.scale_tgt = 1 / (range_vals_tgt[1] - range_vals_tgt[0])
    sb.scale_out = sb.scale_tgt

    sb.bias_inp = range_vals_inp[2] * sb.scale_inp
    sb.bias_tgt = range_vals_tgt[2] * sb.scale_tgt
    sb.bias_out = sb.bias_tgt

    return sb

get_normalization_range

get_normalization_range(
    vol: NDArray, percentile: float | None = None
) -> tuple[float, float, float]

Calculate the normalization range for a given volume.

Parameters:

  • vol (NDArray) –

    The input volume as a NumPy array.

  • percentile (float, default: None ) –

    The percentile to use for calculating the normalization range. If None, the minimum, maximum, and mean of the entire volume are used. Default is None.

Returns:

  • tuple[float, float, float]

    A tuple containing the minimum, maximum, and mean values of the volume within the specified percentile range. If percentile is None, the minimum, maximum, and mean of the entire volume are returned.

Notes

If percentile is provided, the function calculates the indices for the minimum and maximum values based on the specified percentile. The mean value is then calculated from the range between these indices.

Source code in src/autoden/algorithms/denoiser.py
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
def get_normalization_range(vol: NDArray, percentile: float | None = None) -> tuple[float, float, float]:
    """
    Calculate the normalization range for a given volume.

    Parameters
    ----------
    vol : NDArray
        The input volume as a NumPy array.
    percentile : float, optional
        The percentile to use for calculating the normalization range. If None, the
        minimum, maximum, and mean of the entire volume are used. Default is None.

    Returns
    -------
    tuple[float, float, float]
        A tuple containing the minimum, maximum, and mean values of the volume within
        the specified percentile range. If `percentile` is None, the minimum, maximum,
        and mean of the entire volume are returned.

    Notes
    -----
    If `percentile` is provided, the function calculates the indices for the minimum
    and maximum values based on the specified percentile. The mean value is then
    calculated from the range between these indices.
    """
    if percentile is not None:
        vol_sort = np.sort(vol.flatten())
        ind_min = int(np.fmax(vol_sort.size * percentile, 0))
        ind_max = int(np.fmin(vol_sort.size * (1 - percentile), vol_sort.size - 1))
        return vol_sort[ind_min], vol_sort[ind_max], vol_sort[ind_min : ind_max + 1].mean()
    else:
        return vol.min(), vol.max(), vol.mean()

get_random_image_indices

get_random_image_indices(
    num_imgs: int, num_tst_ratio: float
) -> list[int]

Return a list of random indices from 0 to num_imgs - 1.

Parameters:

  • num_imgs (int) –

    Total number of images.

  • num_tst_ratio (float) –

    Ratio of images to select.

Returns:

  • list

    List of random indices.

Source code in src/autoden/algorithms/denoiser.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
def get_random_image_indices(num_imgs: int, num_tst_ratio: float) -> list[int]:
    """Return a list of random indices from 0 to num_imgs - 1.

    Parameters
    ----------
    num_imgs : int
        Total number of images.
    num_tst_ratio : float
        Ratio of images to select.

    Returns
    -------
    list
        List of random indices.
    """
    num_tst_imgs = int(num_imgs * num_tst_ratio)
    return list(np.random.choice(num_imgs, size=num_tst_imgs, replace=False))

get_random_pixel_mask

get_random_pixel_mask(
    data_shape: Sequence[int] | NDArray,
    mask_pixel_ratio: float,
) -> NDArray

Generate a random pixel mask for a given data shape.

This function creates a mask where a specified ratio of pixels are set to True, effectively masking those pixels. The remaining pixels are set to True.

Parameters:

  • data_shape (Sequence[int] | NDArray) –

    The shape of the data array for which the mask is to be generated.

  • mask_pixel_ratio (float) –

    The ratio of pixels to be masked (set to True). Must be between 0 and 1.

Returns:

  • NDArray

    A boolean array of the same shape as data_shape with the specified ratio of pixels set to True.

Examples:

>>> data_shape = (10, 10)
>>> mask_pixel_ratio = 0.1
>>> mask = get_random_pixel_mask(data_shape, mask_pixel_ratio)
>>> print(mask)
Source code in src/autoden/algorithms/denoiser.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
def get_random_pixel_mask(data_shape: Sequence[int] | NDArray, mask_pixel_ratio: float) -> NDArray:
    """
    Generate a random pixel mask for a given data shape.

    This function creates a mask where a specified ratio of pixels are set to True,
    effectively masking those pixels. The remaining pixels are set to True.

    Parameters
    ----------
    data_shape : Sequence[int] | NDArray
        The shape of the data array for which the mask is to be generated.
    mask_pixel_ratio : float
        The ratio of pixels to be masked (set to True). Must be between 0 and 1.

    Returns
    -------
    NDArray
        A boolean array of the same shape as `data_shape` with the specified ratio
        of pixels set to True.

    Examples
    --------
    >>> data_shape = (10, 10)
    >>> mask_pixel_ratio = 0.1
    >>> mask = get_random_pixel_mask(data_shape, mask_pixel_ratio)
    >>> print(mask)
    """
    data_mask = np.zeros(data_shape, dtype=bool)
    rnd_inds = np.random.randint(low=0, high=data_mask.size, size=int(data_mask.size * mask_pixel_ratio))
    data_mask.flat[rnd_inds] = True
    return data_mask