From 163f50bdad36e0ab44e7d8dc3b7f82263fe158f4 Mon Sep 17 00:00:00 2001 From: mirjagranfors Date: Wed, 30 Jul 2025 11:09:34 +0200 Subject: [PATCH 01/33] update of background noise --- deeptrack/noises.py | 59 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/deeptrack/noises.py b/deeptrack/noises.py index 2eb937724..f8cad7f76 100644 --- a/deeptrack/noises.py +++ b/deeptrack/noises.py @@ -63,14 +63,42 @@ 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 + """Adds a constant value to an image. Parameters ---------- offset : float - The value to add to the image + 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 + >>> import numpy as np + + Create an input image with zeros: + >>> 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__( @@ -78,6 +106,16 @@ def __init__( 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( @@ -86,6 +124,21 @@ def get( offset: float, **kwargs: Any, ) -> NDArray[Any] | torch.Tensor | Image: + """ + Add the given offset to the image. + + 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 From 905d6efa0bd1f119fff2ff2b9ff8c6b9cb1baa94 Mon Sep 17 00:00:00 2001 From: mirjagranfors Date: Wed, 30 Jul 2025 11:09:50 +0200 Subject: [PATCH 02/33] update of background noises unittest --- deeptrack/tests/test_noises.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/deeptrack/tests/test_noises.py b/deeptrack/tests/test_noises.py index 964e0411a..34c8e6cc6 100644 --- a/deeptrack/tests/test_noises.py +++ b/deeptrack/tests/test_noises.py @@ -7,26 +7,49 @@ import numpy as np from deeptrack.image import Image -from deeptrack import noises +from deeptrack import noises, TORCH_AVAILABLE +if TORCH_AVAILABLE: + import torch class TestNoises(unittest.TestCase): def test_Offset(self): noise = noises.Offset(offset=0.5) input_image = Image(np.zeros((256, 256))) output_image = noise.resolve(input_image) + self.assertIsInstance(output_image, np.ndarray) self.assertEqual(output_image.shape, (256, 256)) self.assertTrue(np.all(np.array(output_image) == 0.5)) def test_Background(self): + # Test with DeepTrack Image noise = noises.Background(offset=0.5) input_image = Image(np.zeros((256, 256))) output_image = noise.resolve(input_image) + self.assertIsInstance(output_image, np.ndarray) self.assertEqual(output_image.shape, (256, 256)) self.assertTrue(np.all(np.array(output_image) == 0.5)) + # Test with NumPy array + noise = noises.Background(offset=0.5) + input_image = np.ones((10, 10)) + output_image = noise.resolve(input_image) + self.assertIsInstance(output_image, np.ndarray) + self.assertEqual(output_image.shape, (10, 10)) + self.assertTrue(np.all(np.array(output_image) == 1.5)) + + ### Test with PyTorch tensor (if available) + if TORCH_AVAILABLE: + noise = noises.Background(offset=0.25) + input_image = torch.zeros(5,5) + output_image = noise.resolve(input_image) + + self.assertIsInstance(output_image, torch.Tensor) + self.assertEqual(output_image.shape, (5,5)) + self.assertTrue(torch.all(output_image == 0.25).item()) + def test_Gaussian(self): noise = noises.Gaussian(mu=0.1, sigma=0.05) input_image = Image(np.zeros((256, 256))) From 284cd18b81e7ab67eff6bc123b613546b5a7867c Mon Sep 17 00:00:00 2001 From: mirjagranfors Date: Tue, 12 Aug 2025 14:00:52 +0200 Subject: [PATCH 03/33] update noises background --- deeptrack/noises.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/deeptrack/noises.py b/deeptrack/noises.py index f8cad7f76..5a50f39d6 100644 --- a/deeptrack/noises.py +++ b/deeptrack/noises.py @@ -64,13 +64,13 @@ class Noise(Feature): class Background(Noise): - """Adds a constant value to an image. + """Add a constant value to an image. Parameters ---------- - offset : float + offset: float The value to add to the image. - **kwargs : Any + **kwargs: Any Additional keyword arguments passed to the parent `Noise` class. Methods @@ -85,9 +85,10 @@ class Background(Noise): Examples -------- >>> import deeptrack as dt - >>> import numpy as np 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: @@ -106,14 +107,13 @@ def __init__( offset: PropertyLike[float], **kwargs: Any, ): - """ - Initialize the Background noise feature. + """Initialize the Background noise feature. Parameters ---------- - offset : PropertyLike[float] + offset: PropertyLike[float] The constant value to be added to the image. - **kwargs : Any + **kwargs: Any Additional arguments passed to the parent `Noise` class. """ super().__init__(offset=offset, **kwargs) @@ -124,14 +124,13 @@ def get( offset: float, **kwargs: Any, ) -> NDArray[Any] | torch.Tensor | Image: - """ - Add the given offset to the image. + """Add the given offset to the image. Parameters ---------- - image : np.ndarray, torch.Tensor, or Image + image: np.ndarray, torch.Tensor, or Image The input image. - offset : float + offset: float The value to add to the image. Returns From b3bc55b9374418c05f583e49a92068e04f6a958a Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Tue, 19 Aug 2025 13:51:30 +0200 Subject: [PATCH 04/33] unit testing now iwth BackendTestBase, for both numpy and torch. --- deeptrack/tests/test_noises.py | 42 +++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/deeptrack/tests/test_noises.py b/deeptrack/tests/test_noises.py index 34c8e6cc6..b29ada1ea 100644 --- a/deeptrack/tests/test_noises.py +++ b/deeptrack/tests/test_noises.py @@ -7,12 +7,17 @@ import numpy as np from deeptrack.image import Image -from deeptrack import noises, TORCH_AVAILABLE +from deeptrack import noises + +from deeptrack.backend import TORCH_AVAILABLE +from deeptrack.tests import BackendTestBase if TORCH_AVAILABLE: import torch -class TestNoises(unittest.TestCase): +class TestNoises_Numpy(BackendTestBase): + BACKEND = "numpy" + def test_Offset(self): noise = noises.Offset(offset=0.5) input_image = Image(np.zeros((256, 256))) @@ -40,16 +45,6 @@ def test_Background(self): self.assertEqual(output_image.shape, (10, 10)) self.assertTrue(np.all(np.array(output_image) == 1.5)) - ### Test with PyTorch tensor (if available) - if TORCH_AVAILABLE: - noise = noises.Background(offset=0.25) - input_image = torch.zeros(5,5) - output_image = noise.resolve(input_image) - - self.assertIsInstance(output_image, torch.Tensor) - self.assertEqual(output_image.shape, (5,5)) - self.assertTrue(torch.all(output_image == 0.25).item()) - def test_Gaussian(self): noise = noises.Gaussian(mu=0.1, sigma=0.05) input_image = Image(np.zeros((256, 256))) @@ -72,6 +67,27 @@ def test_Poisson(self): self.assertIsInstance(output_image, np.ndarray) self.assertEqual(output_image.shape, (256, 256)) +# Extending the test and setting the backend to torch +@unittest.skipUnless(TORCH_AVAILABLE, "PyTorch is not installed.") +class TestNoises_Torch(TestNoises_Numpy): + BACKEND = "torch" + + def test_Backgroud(self): + noise = noises.Background(offset=0.25) + input_image = torch.zeros(5,5) + output_image = noise.resolve(input_image) + + self.assertIsInstance(output_image, torch.Tensor) + self.assertEqual(output_image.shape, (5,5)) + self.assertTrue(torch.all(output_image == 0.25).item()) + + def test_Poisson(self): + noise = noises.Poisson(snr=20) + input_image = torch.ones((256, 256)) * 0.1 + output_image = noise.resolve(input_image) + self.assertIsInstance(output_image, torch.Tensor) + self.assertEqual(output_image.shape, (256, 256)) + if __name__ == "__main__": - unittest.main() \ No newline at end of file + unittest.main() From 1ddb5b4e747411449ac34e2b334838ac1845530a Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Tue, 19 Aug 2025 05:18:32 -0700 Subject: [PATCH 05/33] Added docs and torch implementation for Poisson --- deeptrack/noises.py | 120 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 111 insertions(+), 9 deletions(-) diff --git a/deeptrack/noises.py b/deeptrack/noises.py index 5a50f39d6..bc6071087 100644 --- a/deeptrack/noises.py +++ b/deeptrack/noises.py @@ -215,21 +215,79 @@ def get( return noisy_image -#TODO ***AL*** revise Poisson - torch, typing, docstring, unit test class Poisson(Noise): """Adds 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`. + Depending on the backend it will return either an `Image` object for Numpy + or a `torch.Tensor` for Torch. + 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. + + Methods + ------- + _get_numpy( + image: np.ndarray or Image, + snr: float, + background: float, + max_val: float, optional, + **kwargs, + ) -> np.ndarray or Image + Internal function to add the Poisson noise to the image as a + numpy array or Image. + + _get_torch( + image: torch.Tensor, + snr: float, + background: float, + max_val: float, optional, + **kwargs, + ) -> torch.Tensor, np.ndarray, or Image + Internal function to add the Poisson noise to the image as a + torch.Tensor. + + get( + image: torch.Tensor, + snr: float, + background: float, + max_val: float, optional, + **kwargs, + ) -> torch.Tensor + Depending on the backend will call either `_get_numpy` or `_get_torch` + and return 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__( @@ -249,27 +307,71 @@ def __init__( **kwargs, ) - def get( + def _get_numpy( self: Poisson, - image: NDArray[Any] | torch.Tensor | Image, + image: NDArray[Any] | Image, snr: float, background: float, max_val: float, **kwargs: Any, - ) -> NDArray[Any] | torch.Tensor | Image: + ) -> NDArray[Any] | Image: + """`Get` method for a numpy backend""" 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)) + 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! + 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." ) + + def _get_torch( + self: Poisson, + image: torch.Tensor, + snr: float, + background: float, + max_val: float, + **kwargs: Any, + ) -> torch.Tensor | Image: + """`Get` method for a torch backend""" + + 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." + ) + + def get( + self: Poisson, + image: NDArray[Any] | torch.Tensor | Image, + snr: float, + background: float, + max_val: float, + **kwargs: Any, + ) -> NDArray[Any] | torch.Tensor | Image: + + if self.get_backend() == "numpy": + return self._get_numpy(image, snr, background, max_val, **kwargs,) + elif self.get_backend() == "torch": + return self._get_torch(image, snr, background, max_val, **kwargs,) \ No newline at end of file From 74d390585355c47991dc629fd869804c05374779 Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Wed, 20 Aug 2025 10:03:32 +0200 Subject: [PATCH 06/33] modified get function for Poisson --- deeptrack/noises.py | 116 ++++++++++++++------------------------------ 1 file changed, 37 insertions(+), 79 deletions(-) diff --git a/deeptrack/noises.py b/deeptrack/noises.py index bc6071087..40e335314 100644 --- a/deeptrack/noises.py +++ b/deeptrack/noises.py @@ -238,25 +238,6 @@ class Poisson(Noise): Methods ------- - _get_numpy( - image: np.ndarray or Image, - snr: float, - background: float, - max_val: float, optional, - **kwargs, - ) -> np.ndarray or Image - Internal function to add the Poisson noise to the image as a - numpy array or Image. - - _get_torch( - image: torch.Tensor, - snr: float, - background: float, - max_val: float, optional, - **kwargs, - ) -> torch.Tensor, np.ndarray, or Image - Internal function to add the Poisson noise to the image as a - torch.Tensor. get( image: torch.Tensor, @@ -264,9 +245,9 @@ class Poisson(Noise): background: float, max_val: float, optional, **kwargs, - ) -> torch.Tensor - Depending on the backend will call either `_get_numpy` or `_get_torch` - and return an image with Poisson noise added. + ) -> NDArray, Image, or torch.Tensor + Depending on the backend will perform either the numpy or torch + implementationo and return an image with Poisson noise added. Examples -------- @@ -307,61 +288,6 @@ def __init__( **kwargs, ) - def _get_numpy( - self: Poisson, - image: NDArray[Any] | Image, - snr: float, - background: float, - max_val: float, - **kwargs: Any, - ) -> NDArray[Any] | Image: - """`Get` method for a numpy backend""" - - 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." - ) - - def _get_torch( - self: Poisson, - image: torch.Tensor, - snr: float, - background: float, - max_val: float, - **kwargs: Any, - ) -> torch.Tensor | Image: - """`Get` method for a torch backend""" - - 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." - ) - def get( self: Poisson, image: NDArray[Any] | torch.Tensor | Image, @@ -372,6 +298,38 @@ def get( ) -> NDArray[Any] | torch.Tensor | Image: if self.get_backend() == "numpy": - return self._get_numpy(image, snr, background, max_val, **kwargs,) + 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." + ) + elif self.get_backend() == "torch": - return self._get_torch(image, snr, background, max_val, **kwargs,) \ No newline at end of file + 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." + ) \ No newline at end of file From 9d0e7b79e672d58195a359bf110db9d9e5bef0df Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Wed, 20 Aug 2025 10:09:29 +0200 Subject: [PATCH 07/33] docs polishing --- deeptrack/noises.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/deeptrack/noises.py b/deeptrack/noises.py index 40e335314..54731b9b6 100644 --- a/deeptrack/noises.py +++ b/deeptrack/noises.py @@ -247,7 +247,7 @@ class Poisson(Noise): **kwargs, ) -> NDArray, Image, or torch.Tensor Depending on the backend will perform either the numpy or torch - implementationo and return an image with Poisson noise added. + implementation and return an image with Poisson noise added. Examples -------- @@ -297,6 +297,7 @@ def get( **kwargs: Any, ) -> NDArray[Any] | torch.Tensor | Image: + # For a numpy backend. if self.get_backend() == "numpy": image[image < 0] = 0 image_max = np.max(image) @@ -316,6 +317,7 @@ def get( "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) From e6855dd714c2e505814e5388b9542e14ecf1b180 Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Wed, 20 Aug 2025 11:31:51 +0200 Subject: [PATCH 08/33] Implemented Gaussian and Complex Gaussian --- deeptrack/noises.py | 121 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 100 insertions(+), 21 deletions(-) diff --git a/deeptrack/noises.py b/deeptrack/noises.py index 54731b9b6..e790a5b3a 100644 --- a/deeptrack/noises.py +++ b/deeptrack/noises.py @@ -141,20 +141,55 @@ def get( return image + offset - Offset = Background - -#TODO ***JH*** revise Gaussian - torch, typing, docstring, unit test class Gaussian(Noise): """Adds IID Gaussian noise to an image. + Gaussian noise is sampled from a Gaussian distribution and added pixel-wise + to the input image. Depending on the backend it will return either an + `Image` object for Numpy or a `torch.Tensor` for Torch. + Parameters ---------- - mu : float - The mean of the Gaussian distribution. - sigma : float - The standard deviation of the Gaussian distribution. + mu: float + the mean of the Gaussian distribution. + sigma: float + The standard deviation of the Gaussian distribution + + Methods + ------- + + get( + image: torch.Tensor, + snr: float, + background: float, + max_val: float, optional, + **kwargs, + ) -> NDArray, Image, or torch.Tensor + Depending on the backend will perform either the numpy or torch + implementation and return 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 a standard deviation of 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__( @@ -163,7 +198,6 @@ def __init__( sigma: PropertyLike[float] = 1, **kwargs: Any, ): - super().__init__(mu=mu, sigma=sigma, **kwargs) def get( @@ -174,21 +208,62 @@ 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. + Gaussian noise is sampled from a Gaussian distribution and added pixel-wise + to the input image. Depending on the backend it will return either an + `Image` object for Numpy or a `torch.Tensor` for Torch. + Parameters ---------- - mu : float - The mean of the Gaussian distribution. - sigma : float - The standard deviation of the Gaussian distribution. + mu: float + the mean of the Gaussian distribution. + sigma: float + The standard deviation of the Gaussian distribution + + Methods + ------- + + get( + image: torch.Tensor, + snr: float, + background: float, + max_val: float, optional, + **kwargs, + ) -> NDArray, Image, or torch.Tensor + Depending on the backend will perform either the numpy or torch + implementation and return 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 a standard deviation of 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__( @@ -197,7 +272,6 @@ def __init__( sigma: PropertyLike[float] = 1, **kwargs: Any, ): - super().__init__(mu=mu, sigma=sigma, **kwargs) def get( @@ -208,10 +282,16 @@ 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 - + 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 + + 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 @@ -279,7 +359,6 @@ def __init__( max_val: PropertyLike[float] = 1e8, **kwargs, ): - super().__init__( *args, snr=snr, From a7fbb724b94b767c2bc897dabbe5a29633929557 Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Wed, 20 Aug 2025 11:35:03 +0200 Subject: [PATCH 09/33] Docs polishing --- deeptrack/noises.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/deeptrack/noises.py b/deeptrack/noises.py index e790a5b3a..c5c4b6d80 100644 --- a/deeptrack/noises.py +++ b/deeptrack/noises.py @@ -220,7 +220,8 @@ def get( class ComplexGaussian(Noise): """Adds complex-valued IID Gaussian noise to an image. - Gaussian noise is sampled from a Gaussian distribution and added pixel-wise + 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. Depending on the backend it will return either an `Image` object for Numpy or a `torch.Tensor` for Torch. @@ -282,11 +283,12 @@ def get( **kwargs: Any, ) -> NDArray[Any] | torch.Tensor | Image: + # 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 From cf49b9dfa049affb0ba5643eb8f262efa454c22d Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Wed, 20 Aug 2025 11:41:09 +0200 Subject: [PATCH 10/33] Torch tests for Gaussian & Complex Gaussian --- deeptrack/tests/test_noises.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/deeptrack/tests/test_noises.py b/deeptrack/tests/test_noises.py index b29ada1ea..52723be7e 100644 --- a/deeptrack/tests/test_noises.py +++ b/deeptrack/tests/test_noises.py @@ -80,6 +80,21 @@ def test_Backgroud(self): self.assertIsInstance(output_image, torch.Tensor) self.assertEqual(output_image.shape, (5,5)) self.assertTrue(torch.all(output_image == 0.25).item()) + + def test_Gaussian(self): + noise = noises.Gaussian(mu=0.1, sigma=0.05) + input_image = torch.zeros((256, 256)) + output_image = noise.resolve(input_image) + self.assertIsInstance(output_image, torch.Tensor) + self.assertEqual(output_image.shape, (256, 256)) + + def test_ComplexGaussian(self): + noise = noises.ComplexGaussian(mu=0.1, sigma=0.05) + input_image = torch.zeros((256, 256)) + output_image = noise.resolve(input_image) + self.assertIsInstance(output_image, torch.Tensor) + self.assertEqual(output_image.shape, (256, 256)) + self.assertTrue(torch.is_complex(output_image)) def test_Poisson(self): noise = noises.Poisson(snr=20) From 91b4b4b04ec955f959551e2f2c416c15a2685969 Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Wed, 20 Aug 2025 11:44:03 +0200 Subject: [PATCH 11/33] syntax --- deeptrack/noises.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deeptrack/noises.py b/deeptrack/noises.py index c5c4b6d80..f498cbb67 100644 --- a/deeptrack/noises.py +++ b/deeptrack/noises.py @@ -213,7 +213,7 @@ def get( 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 + noisy_image = mu + image + torch.randn(*image.shape) * sigma return noisy_image From 870ee42af7532dda7a014463125b2c01297d300a Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Fri, 22 Aug 2025 14:11:03 +0200 Subject: [PATCH 12/33] Implemented Feedback from Mirja --- deeptrack/noises.py | 69 +++++++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 27 deletions(-) diff --git a/deeptrack/noises.py b/deeptrack/noises.py index f498cbb67..8e27c3660 100644 --- a/deeptrack/noises.py +++ b/deeptrack/noises.py @@ -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. @@ -144,36 +143,40 @@ def get( Offset = Background 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 + Gaussian noise is sampled from a Gaussian distribution and added pixel-wise to the input image. Depending on the backend it will return either an `Image` object for Numpy or a `torch.Tensor` for Torch. Parameters ---------- mu: float - the mean of the Gaussian distribution. + The mean of the Gaussian distribution. sigma: float - The standard deviation of the Gaussian distribution + 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: torch.Tensor, + image: np.ndarray, torch.Tensor, or Image, snr: float, background: float, max_val: float, optional, **kwargs, - ) -> NDArray, Image, or torch.Tensor - Depending on the backend will perform either the numpy or torch - implementation and return an image with Gaussian noise added. + ) -> 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: @@ -181,7 +184,7 @@ class Gaussian(Noise): >>> >>> input_image = np.ones((2,2)) * 3 - Define the Gaussian noise feature with a standard deviation of 0.1: + 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: @@ -211,6 +214,7 @@ def get( # 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 @@ -218,7 +222,7 @@ def get( return noisy_image 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 @@ -230,20 +234,25 @@ class ComplexGaussian(Noise): mu: float the mean of the Gaussian distribution. sigma: float - The standard deviation of the Gaussian distribution + 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: torch.Tensor, + image: np.ndarray, torch.Tensor, or Image, snr: float, background: float, max_val: float, optional, **kwargs, - ) -> NDArray, Image, or torch.Tensor - Depending on the backend will perform either the numpy or torch - implementation and return an image with complex Gaussian noise added. + ) -> np.ndarray, torch.Tensor, or Image + Returns an image with complex Gaussian noise added. Examples -------- @@ -256,7 +265,7 @@ class ComplexGaussian(Noise): >>> >>> input_image = np.ones((2,2)) * 3 - Define the Gaussian noise feature with a standard deviation of 0.1: + 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: @@ -288,6 +297,7 @@ def get( 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) @@ -298,7 +308,7 @@ def get( 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 @@ -318,18 +328,23 @@ class Poisson(Noise): 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: torch.Tensor, + image: np.ndarray, torch.Tensor, or Image, snr: float, background: float, max_val: float, optional, **kwargs, - ) -> NDArray, Image, or torch.Tensor - Depending on the backend will perform either the numpy or torch - implementation and return an image with Poisson noise added. + ) -> np.ndarray, torch.Tensor, or Image + Returns an image with Poisson noise added. Examples -------- From d82fe7bc1c469c428a0af7c1af6c59239a0429b0 Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Fri, 22 Aug 2025 14:32:20 +0200 Subject: [PATCH 13/33] docs --- deeptrack/noises.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/deeptrack/noises.py b/deeptrack/noises.py index 8e27c3660..666f40c73 100644 --- a/deeptrack/noises.py +++ b/deeptrack/noises.py @@ -155,7 +155,7 @@ class Gaussian(Noise): The mean of the Gaussian distribution. sigma: float The standard deviation of the Gaussian distribution. - + Notes ----- If the backend is NumPy, the calculations use NumPy-compatible functions, @@ -404,7 +404,9 @@ def get( rescale, 1e-10, max_val / np.abs(image_max) ) try: - noisy_image = Image(np.random.poisson(image * rescale) / rescale) + noisy_image = Image( + np.random.poisson(image * rescale) / rescale + ) noisy_image.merge_properties_from(image) return noisy_image except ValueError: From 5ea4e646c868f72f4498c9f824f69a39b157664e Mon Sep 17 00:00:00 2001 From: mirjagranfors Date: Mon, 25 Aug 2025 11:00:19 +0200 Subject: [PATCH 14/33] update background --- deeptrack/noises.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deeptrack/noises.py b/deeptrack/noises.py index 5a50f39d6..690595197 100644 --- a/deeptrack/noises.py +++ b/deeptrack/noises.py @@ -79,7 +79,7 @@ class Background(Noise): image: np.ndarray, torch.Tensor, or Image, offset: float, **kwargs, - ) -> np.ndarray, torch.Tensor, or Image + ) -> np.ndarray, torch.Tensor, or Image Adds the constant offset to the input image. Examples From 192145dd68b557012024d5b3523af22948161f8a Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Mon, 25 Aug 2025 13:04:29 +0200 Subject: [PATCH 15/33] Implemented second round feedback from Mirja --- deeptrack/noises.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/deeptrack/noises.py b/deeptrack/noises.py index 666f40c73..413c2e299 100644 --- a/deeptrack/noises.py +++ b/deeptrack/noises.py @@ -78,7 +78,7 @@ class Background(Noise): image: np.ndarray, torch.Tensor, or Image, offset: float, **kwargs, - ) -> np.ndarray, torch.Tensor, or Image + ) -> np.ndarray, torch.Tensor, or Image Adds the constant offset to the input image. Examples @@ -146,8 +146,7 @@ class Gaussian(Noise): """Add IID Gaussian noise to an image. Gaussian noise is sampled from a Gaussian distribution and added pixel-wise - to the input image. Depending on the backend it will return either an - `Image` object for Numpy or a `torch.Tensor` for Torch. + to the input image. Parameters ---------- @@ -161,7 +160,7 @@ class Gaussian(Noise): 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." + torch.Tensor. Methods ------- @@ -171,7 +170,7 @@ class Gaussian(Noise): background: float, max_val: float, optional, **kwargs, - ) -> np.ndarray, torch.Tensor, or Image + ) -> np.ndarray, torch.Tensor, or Image Returns an image with Gaussian noise added. Examples @@ -226,13 +225,12 @@ class ComplexGaussian(Noise): 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. Depending on the backend it will return either an - `Image` object for Numpy or a `torch.Tensor` for Torch. + to the input image. Parameters ---------- mu: float - the mean of the Gaussian distribution. + The mean of the Gaussian distribution. sigma: float The standard deviation of the Gaussian distribution. @@ -241,7 +239,7 @@ class ComplexGaussian(Noise): 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." + torch.Tensor. Methods ------- @@ -251,7 +249,7 @@ class ComplexGaussian(Noise): background: float, max_val: float, optional, **kwargs, - ) -> np.ndarray, torch.Tensor, or Image + ) -> np.ndarray, torch.Tensor, or Image Returns an image with complex Gaussian noise added. Examples @@ -313,8 +311,6 @@ class Poisson(Noise): 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`. - Depending on the backend it will return either an `Image` object for Numpy - or a `torch.Tensor` for Torch. Parameters ---------- @@ -333,7 +329,7 @@ class Poisson(Noise): 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." + torch.Tensor. Methods ------- @@ -343,7 +339,7 @@ class Poisson(Noise): background: float, max_val: float, optional, **kwargs, - ) -> np.ndarray, torch.Tensor, or Image + ) -> np.ndarray, torch.Tensor, or Image Returns an image with Poisson noise added. Examples From 10db9a42c0de15efb0a7b3612fd7e2354e4e43cc Mon Sep 17 00:00:00 2001 From: mirjagranfors Date: Mon, 25 Aug 2025 14:22:33 +0200 Subject: [PATCH 16/33] update noises --- deeptrack/noises.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deeptrack/noises.py b/deeptrack/noises.py index 413c2e299..eaaa5afe1 100644 --- a/deeptrack/noises.py +++ b/deeptrack/noises.py @@ -142,6 +142,7 @@ def get( Offset = Background + class Gaussian(Noise): """Add IID Gaussian noise to an image. @@ -220,6 +221,7 @@ def get( return noisy_image + class ComplexGaussian(Noise): """Add complex-valued IID Gaussian noise to an image. From 72a819f7f957081d8e9959054c13df97aa97cbee Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Sun, 7 Sep 2025 12:28:35 +0200 Subject: [PATCH 17/33] Update test_noises.py --- deeptrack/tests/test_noises.py | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/deeptrack/tests/test_noises.py b/deeptrack/tests/test_noises.py index 52723be7e..9af509503 100644 --- a/deeptrack/tests/test_noises.py +++ b/deeptrack/tests/test_noises.py @@ -30,20 +30,20 @@ def test_Offset(self): def test_Background(self): # Test with DeepTrack Image noise = noises.Background(offset=0.5) - input_image = Image(np.zeros((256, 256))) + input_image = Image(xp.zeros((256, 256))) output_image = noise.resolve(input_image) - self.assertIsInstance(output_image, np.ndarray) + self.assertIsInstance(output_image, xp.ndarray) self.assertEqual(output_image.shape, (256, 256)) - self.assertTrue(np.all(np.array(output_image) == 0.5)) + self.assertTrue(xp.all(xp.array(output_image) == 0.5)) # Test with NumPy array noise = noises.Background(offset=0.5) - input_image = np.ones((10, 10)) + input_image = xp.ones((10, 10)) output_image = noise.resolve(input_image) - self.assertIsInstance(output_image, np.ndarray) + self.assertIsInstance(output_image, xp.ndarray) self.assertEqual(output_image.shape, (10, 10)) - self.assertTrue(np.all(np.array(output_image) == 1.5)) + self.assertTrue(xp.all(xp.array(output_image) == 1.5)) def test_Gaussian(self): noise = noises.Gaussian(mu=0.1, sigma=0.05) @@ -71,15 +71,8 @@ def test_Poisson(self): @unittest.skipUnless(TORCH_AVAILABLE, "PyTorch is not installed.") class TestNoises_Torch(TestNoises_Numpy): BACKEND = "torch" - - def test_Backgroud(self): - noise = noises.Background(offset=0.25) - input_image = torch.zeros(5,5) - output_image = noise.resolve(input_image) - - self.assertIsInstance(output_image, torch.Tensor) - self.assertEqual(output_image.shape, (5,5)) - self.assertTrue(torch.all(output_image == 0.25).item()) + pass + def test_Gaussian(self): noise = noises.Gaussian(mu=0.1, sigma=0.05) From b777a7ddbca9cf9c1e4474fad841d0e86010e91f Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Sun, 7 Sep 2025 12:30:52 +0200 Subject: [PATCH 18/33] imports --- deeptrack/tests/test_noises.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deeptrack/tests/test_noises.py b/deeptrack/tests/test_noises.py index 9af509503..a33fa0bd8 100644 --- a/deeptrack/tests/test_noises.py +++ b/deeptrack/tests/test_noises.py @@ -4,12 +4,13 @@ import unittest +import array_api_compat as apc import numpy as np from deeptrack.image import Image from deeptrack import noises -from deeptrack.backend import TORCH_AVAILABLE +from deeptrack.backend import OPENCV_AVAILABLE, TORCH_AVAILABLE, xp from deeptrack.tests import BackendTestBase if TORCH_AVAILABLE: From 85be2794896d48141d13e4917eceb07def64fac8 Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Sun, 7 Sep 2025 12:33:54 +0200 Subject: [PATCH 19/33] Update test_noises.py --- deeptrack/tests/test_noises.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deeptrack/tests/test_noises.py b/deeptrack/tests/test_noises.py index a33fa0bd8..3cb72bc3c 100644 --- a/deeptrack/tests/test_noises.py +++ b/deeptrack/tests/test_noises.py @@ -34,7 +34,7 @@ def test_Background(self): input_image = Image(xp.zeros((256, 256))) output_image = noise.resolve(input_image) - self.assertIsInstance(output_image, xp.ndarray) + self.assertIsInstance(output_image, np.ndarray) self.assertEqual(output_image.shape, (256, 256)) self.assertTrue(xp.all(xp.array(output_image) == 0.5)) From 197ae406941e1bd63859bc196e61ef75befd0e55 Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Sun, 7 Sep 2025 12:37:04 +0200 Subject: [PATCH 20/33] u --- deeptrack/tests/test_noises.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/deeptrack/tests/test_noises.py b/deeptrack/tests/test_noises.py index 3cb72bc3c..206e59ff9 100644 --- a/deeptrack/tests/test_noises.py +++ b/deeptrack/tests/test_noises.py @@ -34,15 +34,16 @@ def test_Background(self): input_image = Image(xp.zeros((256, 256))) output_image = noise.resolve(input_image) - self.assertIsInstance(output_image, np.ndarray) + self.assertIsInstance(type(output_image), type(input_image)) self.assertEqual(output_image.shape, (256, 256)) self.assertTrue(xp.all(xp.array(output_image) == 0.5)) - # Test with NumPy array + # Test with arrays noise = noises.Background(offset=0.5) input_image = xp.ones((10, 10)) output_image = noise.resolve(input_image) - self.assertIsInstance(output_image, xp.ndarray) + + self.assertIsInstance(type(output_image), type(input_image)) self.assertEqual(output_image.shape, (10, 10)) self.assertTrue(xp.all(xp.array(output_image) == 1.5)) From 16e52ecc36d4b3927c20b44882ae5a539157a5a1 Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Sun, 7 Sep 2025 12:39:02 +0200 Subject: [PATCH 21/33] u --- deeptrack/tests/test_noises.py | 1 - 1 file changed, 1 deletion(-) diff --git a/deeptrack/tests/test_noises.py b/deeptrack/tests/test_noises.py index 206e59ff9..802a76ccd 100644 --- a/deeptrack/tests/test_noises.py +++ b/deeptrack/tests/test_noises.py @@ -34,7 +34,6 @@ def test_Background(self): input_image = Image(xp.zeros((256, 256))) output_image = noise.resolve(input_image) - self.assertIsInstance(type(output_image), type(input_image)) self.assertEqual(output_image.shape, (256, 256)) self.assertTrue(xp.all(xp.array(output_image) == 0.5)) From 206c3c86237232922cfc09047859139b562e9033 Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Sun, 7 Sep 2025 12:41:54 +0200 Subject: [PATCH 22/33] u --- deeptrack/tests/test_noises.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/deeptrack/tests/test_noises.py b/deeptrack/tests/test_noises.py index 802a76ccd..2c592dab3 100644 --- a/deeptrack/tests/test_noises.py +++ b/deeptrack/tests/test_noises.py @@ -33,7 +33,8 @@ def test_Background(self): noise = noises.Background(offset=0.5) input_image = Image(xp.zeros((256, 256))) output_image = noise.resolve(input_image) - + + #self.assertIsInstance(output_image, input_image) self.assertEqual(output_image.shape, (256, 256)) self.assertTrue(xp.all(xp.array(output_image) == 0.5)) @@ -42,7 +43,7 @@ def test_Background(self): input_image = xp.ones((10, 10)) output_image = noise.resolve(input_image) - self.assertIsInstance(type(output_image), type(input_image)) + #self.assertIsInstance(output_image, input_image) self.assertEqual(output_image.shape, (10, 10)) self.assertTrue(xp.all(xp.array(output_image) == 1.5)) From be76e34b7cbf4ba24bc80f6aae1cf2cd5f937aa5 Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Sun, 7 Sep 2025 12:44:04 +0200 Subject: [PATCH 23/33] xp.asarray --- deeptrack/tests/test_noises.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deeptrack/tests/test_noises.py b/deeptrack/tests/test_noises.py index 2c592dab3..c8076f3cf 100644 --- a/deeptrack/tests/test_noises.py +++ b/deeptrack/tests/test_noises.py @@ -36,7 +36,7 @@ def test_Background(self): #self.assertIsInstance(output_image, input_image) self.assertEqual(output_image.shape, (256, 256)) - self.assertTrue(xp.all(xp.array(output_image) == 0.5)) + self.assertTrue(xp.all(xp.asarray(output_image) == 0.5)) # Test with arrays noise = noises.Background(offset=0.5) @@ -45,7 +45,7 @@ def test_Background(self): #self.assertIsInstance(output_image, input_image) self.assertEqual(output_image.shape, (10, 10)) - self.assertTrue(xp.all(xp.array(output_image) == 1.5)) + self.assertTrue(xp.all(xp.asarray(output_image) == 1.5)) def test_Gaussian(self): noise = noises.Gaussian(mu=0.1, sigma=0.05) From 7e6cd818775d3d1c090e1d7db93e07a2ffb9783c Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Sun, 7 Sep 2025 13:04:30 +0200 Subject: [PATCH 24/33] rest of the functions --- deeptrack/tests/test_noises.py | 54 +++++++++++----------------------- 1 file changed, 17 insertions(+), 37 deletions(-) diff --git a/deeptrack/tests/test_noises.py b/deeptrack/tests/test_noises.py index c8076f3cf..e6049276f 100644 --- a/deeptrack/tests/test_noises.py +++ b/deeptrack/tests/test_noises.py @@ -18,22 +18,22 @@ class TestNoises_Numpy(BackendTestBase): BACKEND = "numpy" - + def test_Offset(self): noise = noises.Offset(offset=0.5) - input_image = Image(np.zeros((256, 256))) + input_image = Image(xp.zeros((256, 256))) output_image = noise.resolve(input_image) - self.assertIsInstance(output_image, np.ndarray) + #self.assertIsInstance(output_image, np.ndarray) self.assertEqual(output_image.shape, (256, 256)) - self.assertTrue(np.all(np.array(output_image) == 0.5)) + self.assertTrue(xp.all(xp.asarray(output_image) == 0.5)) def test_Background(self): # Test with DeepTrack Image noise = noises.Background(offset=0.5) input_image = Image(xp.zeros((256, 256))) output_image = noise.resolve(input_image) - + #self.assertIsInstance(output_image, input_image) self.assertEqual(output_image.shape, (256, 256)) self.assertTrue(xp.all(xp.asarray(output_image) == 0.5)) @@ -42,62 +42,42 @@ def test_Background(self): noise = noises.Background(offset=0.5) input_image = xp.ones((10, 10)) output_image = noise.resolve(input_image) - + #self.assertIsInstance(output_image, input_image) self.assertEqual(output_image.shape, (10, 10)) self.assertTrue(xp.all(xp.asarray(output_image) == 1.5)) def test_Gaussian(self): noise = noises.Gaussian(mu=0.1, sigma=0.05) - input_image = Image(np.zeros((256, 256))) + input_image = Image(xp.zeros((256, 256))) output_image = noise.resolve(input_image) - self.assertIsInstance(output_image, np.ndarray) + + #self.assertIsInstance(output_image, np.ndarray) self.assertEqual(output_image.shape, (256, 256)) def test_ComplexGaussian(self): noise = noises.ComplexGaussian(mu=0.1, sigma=0.05) - input_image = Image(np.zeros((256, 256))) + input_image = Image(xp.zeros((256, 256))) output_image = noise.resolve(input_image) - self.assertIsInstance(output_image, np.ndarray) + + #self.assertIsInstance(output_image, np.ndarray) self.assertEqual(output_image.shape, (256, 256)) - self.assertTrue(np.iscomplexobj(output_image)) + self.assertTrue(xp.iscomplexobj(output_image)) def test_Poisson(self): noise = noises.Poisson(snr=20) - input_image = Image(np.ones((256, 256)) * 0.1) + input_image = Image(xp.ones((256, 256)) * 0.1) output_image = noise.resolve(input_image) - self.assertIsInstance(output_image, np.ndarray) + + #self.assertIsInstance(output_image, np.ndarray) self.assertEqual(output_image.shape, (256, 256)) + # Extending the test and setting the backend to torch @unittest.skipUnless(TORCH_AVAILABLE, "PyTorch is not installed.") class TestNoises_Torch(TestNoises_Numpy): BACKEND = "torch" pass - - def test_Gaussian(self): - noise = noises.Gaussian(mu=0.1, sigma=0.05) - input_image = torch.zeros((256, 256)) - output_image = noise.resolve(input_image) - self.assertIsInstance(output_image, torch.Tensor) - self.assertEqual(output_image.shape, (256, 256)) - - def test_ComplexGaussian(self): - noise = noises.ComplexGaussian(mu=0.1, sigma=0.05) - input_image = torch.zeros((256, 256)) - output_image = noise.resolve(input_image) - self.assertIsInstance(output_image, torch.Tensor) - self.assertEqual(output_image.shape, (256, 256)) - self.assertTrue(torch.is_complex(output_image)) - - def test_Poisson(self): - noise = noises.Poisson(snr=20) - input_image = torch.ones((256, 256)) * 0.1 - output_image = noise.resolve(input_image) - - self.assertIsInstance(output_image, torch.Tensor) - self.assertEqual(output_image.shape, (256, 256)) - if __name__ == "__main__": unittest.main() From 55a74476b89b1c471f2224257f473a8e98e11110 Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Sun, 7 Sep 2025 13:06:45 +0200 Subject: [PATCH 25/33] removed Image wrapper for Poisson --- deeptrack/tests/test_noises.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deeptrack/tests/test_noises.py b/deeptrack/tests/test_noises.py index e6049276f..6460a3bfe 100644 --- a/deeptrack/tests/test_noises.py +++ b/deeptrack/tests/test_noises.py @@ -66,7 +66,7 @@ def test_ComplexGaussian(self): def test_Poisson(self): noise = noises.Poisson(snr=20) - input_image = Image(xp.ones((256, 256)) * 0.1) + input_image = xp.ones((256, 256)) * 0.1 output_image = noise.resolve(input_image) #self.assertIsInstance(output_image, np.ndarray) From b4a09fbc9b684f2bee62aa753c5faebb4f7f6a4d Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Sun, 7 Sep 2025 13:10:57 +0200 Subject: [PATCH 26/33] u --- deeptrack/tests/test_noises.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deeptrack/tests/test_noises.py b/deeptrack/tests/test_noises.py index 6460a3bfe..ed405694b 100644 --- a/deeptrack/tests/test_noises.py +++ b/deeptrack/tests/test_noises.py @@ -62,7 +62,7 @@ def test_ComplexGaussian(self): #self.assertIsInstance(output_image, np.ndarray) self.assertEqual(output_image.shape, (256, 256)) - self.assertTrue(xp.iscomplexobj(output_image)) + self.assertTrue(xp.iscomplex(output_image)) def test_Poisson(self): noise = noises.Poisson(snr=20) From 9589b4dfaac9a978aeb9bbbd951cab554ade20f3 Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Sun, 7 Sep 2025 13:14:35 +0200 Subject: [PATCH 27/33] complex --- deeptrack/tests/test_noises.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deeptrack/tests/test_noises.py b/deeptrack/tests/test_noises.py index ed405694b..64f49244d 100644 --- a/deeptrack/tests/test_noises.py +++ b/deeptrack/tests/test_noises.py @@ -62,7 +62,7 @@ def test_ComplexGaussian(self): #self.assertIsInstance(output_image, np.ndarray) self.assertEqual(output_image.shape, (256, 256)) - self.assertTrue(xp.iscomplex(output_image)) + self.assertTrue(xp.any(xp.iscomplex(output_image))) def test_Poisson(self): noise = noises.Poisson(snr=20) From 8951fa7036138638603f1db35a35b2e551fb956b Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Sun, 7 Sep 2025 13:16:41 +0200 Subject: [PATCH 28/33] u --- deeptrack/tests/test_noises.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deeptrack/tests/test_noises.py b/deeptrack/tests/test_noises.py index 64f49244d..e59805044 100644 --- a/deeptrack/tests/test_noises.py +++ b/deeptrack/tests/test_noises.py @@ -62,7 +62,7 @@ def test_ComplexGaussian(self): #self.assertIsInstance(output_image, np.ndarray) self.assertEqual(output_image.shape, (256, 256)) - self.assertTrue(xp.any(xp.iscomplex(output_image))) + self.assertTrue(xp.any(xp.is_complex(output_image))) def test_Poisson(self): noise = noises.Poisson(snr=20) From 9a855b331c74396ebe9b1e21deb405ee5acefe6a Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Sun, 7 Sep 2025 13:25:40 +0200 Subject: [PATCH 29/33] Update test_noises.py --- deeptrack/tests/test_noises.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deeptrack/tests/test_noises.py b/deeptrack/tests/test_noises.py index e59805044..c6a3a4f1e 100644 --- a/deeptrack/tests/test_noises.py +++ b/deeptrack/tests/test_noises.py @@ -62,7 +62,8 @@ def test_ComplexGaussian(self): #self.assertIsInstance(output_image, np.ndarray) self.assertEqual(output_image.shape, (256, 256)) - self.assertTrue(xp.any(xp.is_complex(output_image))) + #TODO: Add xp function to check complex values + #self.assertTrue(xp.any(xp.is_complex(output_image))) def test_Poisson(self): noise = noises.Poisson(snr=20) From 19a93d1a39ef892dfdf32a511abfd96e12beee3f Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Sun, 7 Sep 2025 13:33:48 +0200 Subject: [PATCH 30/33] Update test_noises.py --- deeptrack/tests/test_noises.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deeptrack/tests/test_noises.py b/deeptrack/tests/test_noises.py index c6a3a4f1e..6b50117e9 100644 --- a/deeptrack/tests/test_noises.py +++ b/deeptrack/tests/test_noises.py @@ -63,7 +63,7 @@ def test_ComplexGaussian(self): #self.assertIsInstance(output_image, np.ndarray) self.assertEqual(output_image.shape, (256, 256)) #TODO: Add xp function to check complex values - #self.assertTrue(xp.any(xp.is_complex(output_image))) + self.assertTrue(xp.any(output_image.imag != 0)) def test_Poisson(self): noise = noises.Poisson(snr=20) From efd259533d380cce1afc915a697273e42008f37e Mon Sep 17 00:00:00 2001 From: Alex <95913221+Pwhsky@users.noreply.github.com> Date: Sun, 7 Sep 2025 13:38:08 +0200 Subject: [PATCH 31/33] whitespace --- deeptrack/tests/test_noises.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deeptrack/tests/test_noises.py b/deeptrack/tests/test_noises.py index 6b50117e9..bcc592f2e 100644 --- a/deeptrack/tests/test_noises.py +++ b/deeptrack/tests/test_noises.py @@ -59,7 +59,7 @@ def test_ComplexGaussian(self): noise = noises.ComplexGaussian(mu=0.1, sigma=0.05) input_image = Image(xp.zeros((256, 256))) output_image = noise.resolve(input_image) - + #self.assertIsInstance(output_image, np.ndarray) self.assertEqual(output_image.shape, (256, 256)) #TODO: Add xp function to check complex values @@ -69,7 +69,7 @@ def test_Poisson(self): noise = noises.Poisson(snr=20) input_image = xp.ones((256, 256)) * 0.1 output_image = noise.resolve(input_image) - + #self.assertIsInstance(output_image, np.ndarray) self.assertEqual(output_image.shape, (256, 256)) From 79ad87d3139abe86e353e200bbe51ed099a61340 Mon Sep 17 00:00:00 2001 From: mirjagranfors Date: Mon, 8 Sep 2025 09:11:48 +0200 Subject: [PATCH 32/33] added checks for array types --- deeptrack/tests/test_noises.py | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/deeptrack/tests/test_noises.py b/deeptrack/tests/test_noises.py index bcc592f2e..db9c440fb 100644 --- a/deeptrack/tests/test_noises.py +++ b/deeptrack/tests/test_noises.py @@ -4,13 +4,12 @@ import unittest -import array_api_compat as apc import numpy as np from deeptrack.image import Image from deeptrack import noises -from deeptrack.backend import OPENCV_AVAILABLE, TORCH_AVAILABLE, xp +from deeptrack.backend import TORCH_AVAILABLE, xp from deeptrack.tests import BackendTestBase if TORCH_AVAILABLE: @@ -19,12 +18,21 @@ class TestNoises_Numpy(BackendTestBase): BACKEND = "numpy" + @property + def array_type(self): + if self.BACKEND == "numpy": + return np.ndarray + elif self.BACKEND == "torch": + return torch.Tensor + else: + raise ValueError(f"Unsupported backend: {self.BACKEND}") + def test_Offset(self): noise = noises.Offset(offset=0.5) input_image = Image(xp.zeros((256, 256))) output_image = noise.resolve(input_image) - #self.assertIsInstance(output_image, np.ndarray) + self.assertIsInstance(output_image, self.array_type) self.assertEqual(output_image.shape, (256, 256)) self.assertTrue(xp.all(xp.asarray(output_image) == 0.5)) @@ -34,7 +42,7 @@ def test_Background(self): input_image = Image(xp.zeros((256, 256))) output_image = noise.resolve(input_image) - #self.assertIsInstance(output_image, input_image) + self.assertIsInstance(output_image, self.array_type) self.assertEqual(output_image.shape, (256, 256)) self.assertTrue(xp.all(xp.asarray(output_image) == 0.5)) @@ -43,7 +51,7 @@ def test_Background(self): input_image = xp.ones((10, 10)) output_image = noise.resolve(input_image) - #self.assertIsInstance(output_image, input_image) + self.assertIsInstance(output_image, self.array_type) self.assertEqual(output_image.shape, (10, 10)) self.assertTrue(xp.all(xp.asarray(output_image) == 1.5)) @@ -52,7 +60,7 @@ def test_Gaussian(self): input_image = Image(xp.zeros((256, 256))) output_image = noise.resolve(input_image) - #self.assertIsInstance(output_image, np.ndarray) + self.assertIsInstance(output_image, self.array_type) self.assertEqual(output_image.shape, (256, 256)) def test_ComplexGaussian(self): @@ -60,17 +68,21 @@ def test_ComplexGaussian(self): input_image = Image(xp.zeros((256, 256))) output_image = noise.resolve(input_image) - #self.assertIsInstance(output_image, np.ndarray) + self.assertIsInstance(output_image, self.array_type) self.assertEqual(output_image.shape, (256, 256)) - #TODO: Add xp function to check complex values self.assertTrue(xp.any(output_image.imag != 0)) + if self.BACKEND == "numpy": + self.assertTrue(np.iscomplexobj(output_image)) + elif self.BACKEND == "torch": + self.assertTrue(torch.is_complex(output_image)) + def test_Poisson(self): noise = noises.Poisson(snr=20) input_image = xp.ones((256, 256)) * 0.1 output_image = noise.resolve(input_image) - #self.assertIsInstance(output_image, np.ndarray) + self.assertIsInstance(output_image, self.array_type) self.assertEqual(output_image.shape, (256, 256)) From c4b98ccc6ad8f90ff5839ac3bc10cc79cf4a9e63 Mon Sep 17 00:00:00 2001 From: mirjagranfors Date: Mon, 8 Sep 2025 10:30:47 +0200 Subject: [PATCH 33/33] update test noises --- deeptrack/tests/test_noises.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deeptrack/tests/test_noises.py b/deeptrack/tests/test_noises.py index db9c440fb..0e15e800a 100644 --- a/deeptrack/tests/test_noises.py +++ b/deeptrack/tests/test_noises.py @@ -15,7 +15,7 @@ if TORCH_AVAILABLE: import torch -class TestNoises_Numpy(BackendTestBase): +class TestNoises_NumPy(BackendTestBase): BACKEND = "numpy" @property @@ -88,7 +88,7 @@ def test_Poisson(self): # Extending the test and setting the backend to torch @unittest.skipUnless(TORCH_AVAILABLE, "PyTorch is not installed.") -class TestNoises_Torch(TestNoises_Numpy): +class TestNoises_PyTorch(TestNoises_NumPy): BACKEND = "torch" pass