Skip to content

deep_image_prior

Unsupervised denoiser implementation, based on the Deep Image Prior.

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

Classes:

  • DIP

    Deep image prior.

DIP

DIP(
    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 | Sequence[str] | None = None,
    save_epochs_dir: str | None = None,
    verbose: bool = True,
)

Bases: Denoiser

Deep image prior.

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.

  • prepare_data

    Prepare input data.

  • train

    Train the model in an unsupervised manner.

Attributes:

  • n_dims (int) –

    Returns the expected signal dimensions.

Source code in src/autoden/algorithms/denoiser.py
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
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 | Sequence[str] | 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_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 the value associated with "n_dims". Otherwise, it defaults to 2.

Returns:

  • int

    The expected signal dimensions.

infer

infer(inp: NDArray) -> NDArray

Inference, given an initial stack of images.

Parameters:

  • inp (NDArray) –

    The input stack of images

Returns:

  • NDArray

    The denoised stack of images

Source code in src/autoden/algorithms/denoiser.py
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
def infer(self, inp: NDArray) -> NDArray:
    """Inference, given an initial stack of images.

    Parameters
    ----------
    inp : NDArray
        The input stack of images

    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

    inp_t = data_to_tensor(inp, device=self.device, n_dims=self.n_dims)

    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

    return output

prepare_data

prepare_data(
    tgt: NDArray,
    num_tst_ratio: float = 0.2,
    average_redundant: bool = False,
) -> tuple[NDArray, NDArray, NDArray]

Prepare input data.

Parameters:

  • tgt (NDArray) –

    The target image array. The shape of the output noise array will match the spatial dimensions of this array.

  • num_tst_ratio (float, default: 0.2 ) –

    The ratio of the test set size to the total dataset size. Default is 0.2.

  • average_redundant (bool, default: False ) –

    If True, average redundant realizations in the target array to match the expected number of dimensions. Default is False.

Returns:

  • tuple[NDArray, NDArray, NDArray]

    A tuple containing: - A random noise array with the same spatial dimensions as the target image. - The target image array. - A mask array indicating the training pixels.

Notes

This function generates a random noise array with the same spatial dimensions as the target image. The noise array is used as the initial input for the DIP algorithm. It also generates a mask array indicating the training pixels based on the provided ratio.

Source code in src/autoden/algorithms/deep_image_prior.py
23
24
25
26
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
59
60
61
62
63
64
def prepare_data(
    self, tgt: NDArray, num_tst_ratio: float = 0.2, average_redundant: bool = False
) -> tuple[NDArray, NDArray, NDArray]:
    """
    Prepare input data.

    Parameters
    ----------
    tgt : NDArray
        The target image array. The shape of the output noise array will match
        the spatial dimensions of this array.
    num_tst_ratio : float, optional
        The ratio of the test set size to the total dataset size.
        Default is 0.2.
    average_redundant : bool, optional
        If True, average redundant realizations in the target array to match the
        expected number of dimensions. Default is False.

    Returns
    -------
    tuple[NDArray, NDArray, NDArray]
        A tuple containing:
        - A random noise array with the same spatial dimensions as the target
          image.
        - The target image array.
        - A mask array indicating the training pixels.

    Notes
    -----
    This function generates a random noise array with the same spatial dimensions
    as the target image. The noise array is used as the initial input for the DIP
    algorithm. It also generates a mask array indicating the training pixels based
    on the provided ratio.
    """
    if tgt.ndim < self.n_dims:
        raise ValueError(f"Target data should at least be of {self.n_dims} dimensions, but its shape is {tgt.shape}")
    if average_redundant and tgt.ndim > self.n_dims:
        tgt = tgt.mean(axis=tuple(np.arange(-tgt.ndim, -self.n_dims)))

    inp = np.random.normal(size=tgt.shape[-self.n_dims :], scale=0.25).astype(tgt.dtype)
    mask_trn = get_random_pixel_mask(tgt.shape, mask_pixel_ratio=num_tst_ratio)
    return inp, tgt, mask_trn

train

train(
    inp: NDArray,
    tgt: NDArray,
    pixel_mask_trn: NDArray,
    epochs: int,
    optimizer: str = "adam",
    lower_limit: float | NDArray | None = None,
) -> dict[str, NDArray]

Train the model in an unsupervised manner.

Parameters:

  • inp (NDArray) –

    The input image.

  • tgt (NDArray) –

    The target image to be denoised.

  • pixel_mask_trn (NDArray) –

    The mask array indicating the training pixels.

  • epochs (int) –

    The number of training epochs.

  • optimizer (str, default: 'adam' ) –

    The optimization algorithm to use. Default is "adam".

  • lower_limit (float | NDArray | None, default: None ) –

    The lower limit for the input data. If provided, the input data will be clipped to this limit. Default is None.

Returns:

  • dict[str, NDArray]

    A dictionary containing the training losses.

Notes

This method trains the model using the deep image prior approach in an unsupervised manner. It uses a random initialization for the input image if not provided and applies a scaling and bias transformation to the input and target images. It then trains the model using the specified optimization algorithm and the provided mask array indicating the training pixels.

Source code in src/autoden/algorithms/deep_image_prior.py
 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
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
def train(
    self,
    inp: NDArray,
    tgt: NDArray,
    pixel_mask_trn: NDArray,
    epochs: int,
    optimizer: str = "adam",
    lower_limit: float | NDArray | None = None,
) -> dict[str, NDArray]:
    """
    Train the model in an unsupervised manner.

    Parameters
    ----------
    inp : NDArray
        The input image.
    tgt : NDArray
        The target image to be denoised.
    pixel_mask_trn : NDArray
        The mask array indicating the training pixels.
    epochs : int
        The number of training epochs.
    optimizer : str, optional
        The optimization algorithm to use. Default is "adam".
    lower_limit : float | NDArray | None, optional
        The lower limit for the input data. If provided, the input data will be clipped to this limit.
        Default is None.

    Returns
    -------
    dict[str, NDArray]
        A dictionary containing the training losses.

    Notes
    -----
    This method trains the model using the deep image prior approach in an unsupervised manner.
    It uses a random initialization for the input image if not provided and applies a scaling and bias
    transformation to the input and target images. It then trains the model using the specified optimization
    algorithm and the provided mask array indicating the training pixels.
    """
    if self.data_sb is None:
        self.data_sb = compute_scaling_supervised(inp, tgt)

    # Rescale the datasets
    tmp_inp = inp * self.data_sb.scale_inp - self.data_sb.bias_inp
    tmp_tgt = tgt * self.data_sb.scale_tgt - self.data_sb.bias_tgt

    reg = self._get_regularization()
    losses = self._train_pixelmask_small(
        tmp_inp, tmp_tgt, pixel_mask_trn, epochs=epochs, optimizer=optimizer, regularizer=reg, lower_limit=lower_limit
    )

    if self.verbose:
        self._plot_loss_curves(losses, f"Unsupervised {self.__class__.__name__} {optimizer.upper()}")

    return losses