Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
163f50b
update of background noise
mirjagranfors Jul 30, 2025
905d6ef
update of background noises unittest
mirjagranfors Jul 30, 2025
284cd18
update noises background
mirjagranfors Aug 12, 2025
b3bc55b
unit testing now iwth BackendTestBase, for both numpy and torch.
Pwhsky Aug 19, 2025
1ddb5b4
Added docs and torch implementation for Poisson
Pwhsky Aug 19, 2025
74d3905
modified get function for Poisson
Pwhsky Aug 20, 2025
9d0e7b7
docs polishing
Pwhsky Aug 20, 2025
e6855dd
Implemented Gaussian and Complex Gaussian
Pwhsky Aug 20, 2025
a7fbb72
Docs polishing
Pwhsky Aug 20, 2025
cf49b9d
Torch tests for Gaussian & Complex Gaussian
Pwhsky Aug 20, 2025
91b4b4b
syntax
Pwhsky Aug 20, 2025
870ee42
Implemented Feedback from Mirja
Pwhsky Aug 22, 2025
d82fe7b
docs
Pwhsky Aug 22, 2025
5ea4e64
update background
mirjagranfors Aug 25, 2025
192145d
Implemented second round feedback from Mirja
Pwhsky Aug 25, 2025
ad190db
Merge pull request #420 from DeepTrackAI/al/noises/poisson-gaussian
mirjagranfors Aug 25, 2025
10db9a4
update noises
mirjagranfors Aug 25, 2025
72a819f
Update test_noises.py
Pwhsky Sep 7, 2025
b777a7d
imports
Pwhsky Sep 7, 2025
85be279
Update test_noises.py
Pwhsky Sep 7, 2025
197ae40
u
Pwhsky Sep 7, 2025
16e52ec
u
Pwhsky Sep 7, 2025
206c3c8
u
Pwhsky Sep 7, 2025
be76e34
xp.asarray
Pwhsky Sep 7, 2025
7e6cd81
rest of the functions
Pwhsky Sep 7, 2025
55a7447
removed Image wrapper for Poisson
Pwhsky Sep 7, 2025
b4a09fb
u
Pwhsky Sep 7, 2025
9589b4d
complex
Pwhsky Sep 7, 2025
8951fa7
u
Pwhsky Sep 7, 2025
9a855b3
Update test_noises.py
Pwhsky Sep 7, 2025
19a93d1
Update test_noises.py
Pwhsky Sep 7, 2025
efd2595
whitespace
Pwhsky Sep 7, 2025
79ad87d
added checks for array types
mirjagranfors Sep 8, 2025
c4b98cc
update test noises
mirjagranfors Sep 8, 2025
402e8c6
Merge pull request #425 from DeepTrackAI/mg/noises_unittest_typechecking
Pwhsky Sep 8, 2025
aae2766
Merge pull request #424 from DeepTrackAI/al/noises/unit-tests
mirjagranfors Sep 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
294 changes: 252 additions & 42 deletions deeptrack/noises.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""
Features for introducing noise to images.
"""Features for introducing noise to images.

This module provides classes to add various types of noise to images,
including constant offsets, Gaussian noise, and Poisson-distributed noise.
Expand Down Expand Up @@ -63,21 +62,59 @@ class Noise(Feature):
"""Base abstract noise class."""


#TODO ***MG*** revise Background - torch, typing, docstring, unit test
class Background(Noise):
"""Adds a constant value to an image
"""Add a constant value to an image.

Parameters
----------
offset : float
The value to add to the image
offset: float
The value to add to the image.
**kwargs: Any
Additional keyword arguments passed to the parent `Noise` class.

Methods
-------
get(
image: np.ndarray, torch.Tensor, or Image,
offset: float,
**kwargs,
) -> np.ndarray, torch.Tensor, or Image
Adds the constant offset to the input image.

Examples
--------
>>> import deeptrack as dt

Create an input image with zeros:
>>> import numpy as np
>>>
>>> input_image = np.zeros((2,2))

Define the Background noise feature with offset 0.5:
>>> noise = dt.Background(offset=0.5)

Apply the noise to the input image and print the resulting image:
>>> output_image = noise.resolve(input_image)
>>> print(output_image)
[[0.5 0.5]
[0.5 0.5]]

"""

def __init__(
self: Background,
offset: PropertyLike[float],
**kwargs: Any,
):
"""Initialize the Background noise feature.

Parameters
----------
offset: PropertyLike[float]
The constant value to be added to the image.
**kwargs: Any
Additional arguments passed to the parent `Noise` class.
"""
super().__init__(offset=offset, **kwargs)

def get(
Expand All @@ -86,23 +123,76 @@ def get(
offset: float,
**kwargs: Any,
) -> NDArray[Any] | torch.Tensor | Image:
"""Add the given offset to the image.

return image + offset
Parameters
----------
image: np.ndarray, torch.Tensor, or Image
The input image.
offset: float
The value to add to the image.

Returns
-------
np.ndarray, torch.Tensor, or Image
The image with offset added.
"""

return image + offset

Offset = Background


#TODO ***JH*** revise Gaussian - torch, typing, docstring, unit test
class Gaussian(Noise):
"""Adds IID Gaussian noise to an image.
"""Add IID Gaussian noise to an image.

Gaussian noise is sampled from a Gaussian distribution and added pixel-wise
to the input image.

Parameters
----------
mu : float
mu: float
The mean of the Gaussian distribution.
sigma : float
sigma: float
The standard deviation of the Gaussian distribution.

Notes
-----
If the backend is NumPy, the calculations use NumPy-compatible functions,
and the output will be a np.array. If the backend is PyTorch, the
calculations use PyTorch-compatible functions, and the output will be a
torch.Tensor.

Methods
-------
get(
image: np.ndarray, torch.Tensor, or Image,
snr: float,
background: float,
max_val: float, optional,
**kwargs,
) -> np.ndarray, torch.Tensor, or Image
Returns an image with Gaussian noise added.

Examples
--------
Add Gaussian noise to an image.
>>> import deeptrack as dt

Create an input image with constant values:
>>> import numpy as np
>>>
>>> input_image = np.ones((2,2)) * 3

Define the Gaussian noise feature with mean 1 and standard deviation 0.1:
>>> noise = dt.Gaussian(mu=1, sigma=0.1)

Apply the noise to the input image and print the resulting image:
>>> output_image = noise.resolve(input_image)
>>> print(output_image)
[[4.01965863 4.20688642]
[4.02184982 3.87875873]]

"""

def __init__(
Expand All @@ -111,7 +201,6 @@ def __init__(
sigma: PropertyLike[float] = 1,
**kwargs: Any,
):

super().__init__(mu=mu, sigma=sigma, **kwargs)

def get(
Expand All @@ -122,21 +211,69 @@ def get(
**kwargs: Any,
) -> NDArray[Any] | torch.Tensor | Image:

noisy_image = mu + image + np.random.randn(*image.shape) * sigma
# For a Numpy backend.
if self.get_backend() == "numpy":
noisy_image = mu + image + np.random.randn(*image.shape) * sigma

# For a Torch backend.
elif self.get_backend() == "torch":
noisy_image = mu + image + torch.randn(*image.shape) * sigma

return noisy_image


#TODO ***JH*** revise ComplexGaussian - torch, typing, docstring, unit test
class ComplexGaussian(Noise):
"""Adds complex-valued IID Gaussian noise to an image.
"""Add complex-valued IID Gaussian noise to an image.

Complex Gaussian noise is sampled by combining two independent Gaussian
distributions for real and imaginary values and is then added pixel-wise
to the input image.

Parameters
----------
mu : float
mu: float
The mean of the Gaussian distribution.
sigma : float
sigma: float
The standard deviation of the Gaussian distribution.

Notes
-----
If the backend is NumPy, the calculations use NumPy-compatible functions,
and the output will be a np.array. If the backend is PyTorch, the
calculations use PyTorch-compatible functions, and the output will be a
torch.Tensor.

Methods
-------
get(
image: np.ndarray, torch.Tensor, or Image,
snr: float,
background: float,
max_val: float, optional,
**kwargs,
) -> np.ndarray, torch.Tensor, or Image
Returns an image with complex Gaussian noise added.

Examples
--------
Add complex Gaussian noise to an image.

>>> import deeptrack as dt

Create an input image with constant values:
>>> import numpy as np
>>>
>>> input_image = np.ones((2,2)) * 3

Define the Gaussian noise feature with mean 1 and standard deviation 0.1:
>>> noise = dt.ComplexGaussian(mu=1, sigma=0.1)

Apply the noise to the input image and print the resulting image:
>>> output_image = noise.resolve(input_image)
>>> print(output_image)
[[3.79975648-0.06967551j 4.09943404+0.06499738j]
[3.99886747-0.23549974j 4.15725117-0.07847024j]]

"""

def __init__(
Expand All @@ -145,7 +282,6 @@ def __init__(
sigma: PropertyLike[float] = 1,
**kwargs: Any,
):

super().__init__(mu=mu, sigma=sigma, **kwargs)

def get(
Expand All @@ -156,28 +292,78 @@ def get(
**kwargs: Any,
) -> NDArray[Any] | torch.Tensor | Image:

real_noise = np.random.randn(*image.shape)
imag_noise = np.random.randn(*image.shape) * 1j
noisy_image = mu + image + (real_noise + imag_noise) * sigma

# For a Numpy backend.
if self.get_backend() == "numpy":
real_noise = np.random.randn(*image.shape)
imag_noise = np.random.randn(*image.shape) * 1j
noisy_image = mu + image + (real_noise + imag_noise) * sigma

# For a Torch backend.
elif self.get_backend() == "torch":
real_noise = torch.randn(*image.shape)
imag_noise = torch.randn(*image.shape) * 1j
noisy_image = mu + image + (real_noise + imag_noise) * sigma

return noisy_image


#TODO ***AL*** revise Poisson - torch, typing, docstring, unit test
class Poisson(Noise):
"""Adds Poisson-distributed noise to an image.
"""Add Poisson-distributed noise to an image.

Poisson noise is sampled and added pixel-wise depending on the
intensity of the pixel in the original image to achieve a desired
signal-to-noise ratio `snr`.

Parameters
----------
snr : float
snr: float
Signal-to-noise ratio of the final image. The signal is determined
by the peak value of the image.
background : float
background: float
Value to be be used as the background. This is used to calculate the
signal of the image.
max_val : float, optional
max_val: float, optional
Maximum allowable value to prevent overflow in noise computation.
Default is 1e8.

Notes
-----
If the backend is NumPy, the calculations use NumPy-compatible functions,
and the output will be a np.array. If the backend is PyTorch, the
calculations use PyTorch-compatible functions, and the output will be a
torch.Tensor.

Methods
-------
get(
image: np.ndarray, torch.Tensor, or Image,
snr: float,
background: float,
max_val: float, optional,
**kwargs,
) -> np.ndarray, torch.Tensor, or Image
Returns an image with Poisson noise added.

Examples
--------
Add Poisson noise to an image.

>>> import deeptrack as dt

Create an input image with ones:
>>> import numpy as np
>>>
>>> input_image = np.ones((2,2))

Define the Poisson noise feature with a low SNR:
>>> noise = dt.Poisson(snr=1)

Apply the noise to the input image and print the resulting image:
>>> output_image = noise.resolve(input_image)
>>> print(output_image)
[[2. 1.]
[0. 4.]]

"""

def __init__(
Expand All @@ -188,7 +374,6 @@ def __init__(
max_val: PropertyLike[float] = 1e8,
**kwargs,
):

super().__init__(
*args,
snr=snr,
Expand All @@ -206,18 +391,43 @@ def get(
**kwargs: Any,
) -> NDArray[Any] | torch.Tensor | Image:

image[image < 0] = 0
image_max = np.max(image)
peak = np.abs(image_max - background)

rescale = snr ** 2 / peak ** 2
rescale = np.clip(rescale, 1e-10, max_val / np.abs(image_max))
try:
noisy_image = Image(np.random.poisson(image * rescale) / rescale)
noisy_image.merge_properties_from(image) # TODO Should only be done if input is Image!
return noisy_image
except ValueError:
raise ValueError(
"NumPy poisson function errored due to too large value. "
"Set max_val in dt.Poisson to a lower value to fix."
# For a numpy backend.
if self.get_backend() == "numpy":
image[image < 0] = 0
image_max = np.max(image)
peak = np.abs(image_max - background)

rescale = snr ** 2 / peak ** 2
rescale = np.clip(
rescale, 1e-10, max_val / np.abs(image_max)
)
try:
noisy_image = Image(
np.random.poisson(image * rescale) / rescale
)
noisy_image.merge_properties_from(image)
return noisy_image
except ValueError:
raise ValueError(
"NumPy poisson function errored due to too large value. "
"Set max_val in dt.Poisson to a lower value to fix."
)

# For a Torch backend.
elif self.get_backend() == "torch":
image = torch.clamp(image, min=0)
image_max = torch.max(image)
peak = torch.abs(image_max - background)

rescale = snr ** 2 / peak ** 2
rescale = torch.clamp(
rescale, min=1e-10, max=max_val / torch.abs(image_max)
)
try:
noisy_image = torch.poisson(image * rescale) / rescale
return noisy_image
except ValueError:
raise ValueError(
"Torch Poisson function errored due to too large value. "
"Set max_val in dt.Poisson to a lower value to fix."
)
Loading
Loading