From f2d5ddf8f13a795678250f3dbb8bed2184d25210 Mon Sep 17 00:00:00 2001 From: Carlo Date: Fri, 14 Mar 2025 19:42:28 +0100 Subject: [PATCH 01/17] u --- deeptrack/math.py | 1 + 1 file changed, 1 insertion(+) diff --git a/deeptrack/math.py b/deeptrack/math.py index a9029efab..9aa8162a7 100644 --- a/deeptrack/math.py +++ b/deeptrack/math.py @@ -54,6 +54,7 @@ """ +from __future__ import annotations from typing import Callable, List import numpy as np From 7f495ceacb86e0338e389aebb313dad2a575f8ff Mon Sep 17 00:00:00 2001 From: Carlo Date: Sat, 15 Mar 2025 19:55:38 +0100 Subject: [PATCH 02/17] u --- deeptrack/math.py | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/deeptrack/math.py b/deeptrack/math.py index 9aa8162a7..202f4c03f 100644 --- a/deeptrack/math.py +++ b/deeptrack/math.py @@ -5,32 +5,51 @@ blurring, and pooling. These are implemented as subclasses of `Feature` for seamless integration with the feature-based design of the library. +Key Features +------------ +- ** Module Structure ----------------- Classes: - `Clip`: Clip the input values within a specified minimum and maximum range. + - `NormalizeMinMax`: Perform min-max normalization on images. -- `NormalizeStandard`: Normalize images to have mean 0 and - standard deviation 1. + +- `NormalizeStandard`: Normalize images to have mean 0 and standard + deviation 1. + - `NormalizeQuantile`: Normalize images based on specified quantiles. + - `Blur`: Apply a blurring filter to the image. + - `AverageBlur`: Apply average blurring to the image. + - `GaussianBlur`: Apply Gaussian blurring to the image. + - `MedianBlur`: Apply median blurring to the image. + - `Pool`: Apply a pooling function to downsample the image. + - `AveragePooling`: Apply average pooling to the image. + - `MaxPooling`: Apply max pooling to the image. + - `MinPooling`: Apply min pooling to the image. + - `MedianPooling`: Apply median pooling to the image. + - `Resize`: Resize the image to a specified size. + - `BlurCV2`: Apply a blurring filter using OpenCV2. + - `BilateralBlur`: Apply bilateral blurring to preserve edges while smoothing. +Fun -Example -------- +Examples +-------- Define a simple pipeline with mathematical operations: >>> import numpy as np @@ -62,10 +81,10 @@ import skimage import skimage.measure -from . import utils -from .features import Feature -from .image import Image, strip -from .types import PropertyLike +from deeptrack import utils +from deeptrack.features import Feature +from deeptrack.image import Image, strip +from deeptrack.types import PropertyLike class Average(Feature): From be9ce8f9ba0faedc2d367f636beb7fe995266df8 Mon Sep 17 00:00:00 2001 From: Carlo Date: Mon, 17 Mar 2025 09:17:11 +0100 Subject: [PATCH 03/17] u --- deeptrack/math.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/deeptrack/math.py b/deeptrack/math.py index 202f4c03f..ce14566db 100644 --- a/deeptrack/math.py +++ b/deeptrack/math.py @@ -7,7 +7,25 @@ Key Features ------------ -- ** +- **Clipping** + + Restrict image values to a specified range. + +- **Normalization** + + Adjust image values to a common scale. + +- **Blurring** + + Smooth images using various filters. + +- **Pooling** + + Downsample images by applying a function to local regions. + +- **Resizing** + + Change the dimensions of images. Module Structure ----------------- @@ -46,7 +64,10 @@ - `BilateralBlur`: Apply bilateral blurring to preserve edges while smoothing. -Fun +Functions: +---------- + + Examples -------- From 74bfde9c1096a6fba1bbe4646a9a4734c7e34742 Mon Sep 17 00:00:00 2001 From: Carlo Date: Mon, 17 Mar 2025 10:02:14 +0100 Subject: [PATCH 04/17] u --- deeptrack/math.py | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/deeptrack/math.py b/deeptrack/math.py index ce14566db..0a397d972 100644 --- a/deeptrack/math.py +++ b/deeptrack/math.py @@ -7,8 +7,8 @@ Key Features ------------ -- **Clipping** - +- **Clipping** + Restrict image values to a specified range. - **Normalization** @@ -64,29 +64,21 @@ - `BilateralBlur`: Apply bilateral blurring to preserve edges while smoothing. -Functions: ----------- - - Examples -------- Define a simple pipeline with mathematical operations: - +>>> import deeptrack as dt >>> import numpy as np ->>> from deeptrack import math Create features for clipping and normalization: - ->>> clip = math.Clip(min=0, max=200) ->>> normalize = math.NormalizeMinMax() +>>> clip = Clip(min=0, max=200) +>>> normalize = NormalizeMinMax() Chain features together: - >>> pipeline = clip >> normalize Process an input image: - >>> input_image = np.array([0, 100, 200, 400]) >>> output_image = pipeline(input_image) >>> print(output_image) @@ -109,7 +101,7 @@ class Average(Feature): - """Average of input images + """Average of input images. If `features` is not None, it instead resolves all features in the list and averages the result. From ffb6e2e9f72a6372bce42409e30b35553d7c9769 Mon Sep 17 00:00:00 2001 From: Carlo Date: Mon, 24 Mar 2025 16:14:34 +0100 Subject: [PATCH 05/17] u --- deeptrack/math.py | 80 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 68 insertions(+), 12 deletions(-) diff --git a/deeptrack/math.py b/deeptrack/math.py index 0a397d972..3877fdd72 100644 --- a/deeptrack/math.py +++ b/deeptrack/math.py @@ -64,7 +64,6 @@ - `BilateralBlur`: Apply bilateral blurring to preserve edges while smoothing. - Examples -------- Define a simple pipeline with mathematical operations: @@ -72,8 +71,8 @@ >>> import numpy as np Create features for clipping and normalization: ->>> clip = Clip(min=0, max=200) ->>> normalize = NormalizeMinMax() +>>> clip = dt.Clip(min=0, max=200) +>>> normalize = dt.NormalizeMinMax() Chain features together: >>> pipeline = clip >> normalize @@ -103,30 +102,85 @@ class Average(Feature): """Average of input images. - If `features` is not None, it instead resolves all features - in the list and averages the result. + If `features` is not None, it instead resolves all features in the list and + averages the result. Parameters ---------- axis: int or tuple of ints Axis along which to average features: list of features, optional + + Attributes + ---------- + __distributed__: bool + Determines whether `.get(image, **kwargs)` is applied to each element + of the input list independently (`__distributed__ = True`) or to the + list as a whole (`__distributed__ = False`). + + Methods + ------- + + `get(images: np.ndarray, axis: int, **kwargs: dict[str, Any]) --> Image` + Computes the average of the input images along the specified axis. + """ __distributed__ = False def __init__( - self, - features=PropertyLike[List[Feature] or None], + self: Average, + features: PropertyLike[list[Feature] | None] = None, axis: PropertyLike[int] = 0, - **kwargs + **kwargs: dict[str, Any] ): + """Initializes the Average feature. + + This constructor initializes the parameters for averaging input + features. + + Parameters + ---------- + features: list of Feature or None, optional + List of features to be resolved and averaged. Defaults to None. + axis: int or tuple[int] + Axis along which to compute the average. Defaults to 0. + **kwargs: dict[str, Any] + Additional keyword arguments. + + """ super().__init__(axis=axis, **kwargs) - if features is not None: - self.features = [self.add_feature(feature) for feature in features] + if features is None: + self.features = None + else: + self.features = [self.add_feature(f) for f in features] + + def get( + self: Average, + images: np.ndarray | Image | list[Image], + axis: int, + **kwargs: dict[str, Any], + ) -> Image: + """Computes the average of input images along the specified axis. + + + This method computes the average of the input images along the + specified axis. + + Parameters + ---------- + images: np.ndarray + The input images to average. + axis: int + The axis along which to average. - def get(self, images, axis, **kwargs): + Returns + ------- + Image + The averaged image. + + """ if self.features is not None: images = [feature.resolve() for feature in self.features] result = Image(np.mean(images, axis=axis)) @@ -146,6 +200,7 @@ class Clip(Feature): Clip the input to be larger than this value. max: float Clip the input to be smaller than this value. + """ def __init__( @@ -173,7 +228,8 @@ class NormalizeMinMax(Feature): max: float The maximum of the transformation. featurewise: bool - Whether to normalize each feature independently + Whether to normalize each feature independently. + """ def __init__( From fa095d3f70dc4df996f514a0ef835a3e99d04ee6 Mon Sep 17 00:00:00 2001 From: Carlo Date: Wed, 26 Mar 2025 17:15:29 +0100 Subject: [PATCH 06/17] u --- deeptrack/math.py | 20 +++++++++++++++++--- deeptrack/tests/test_math.py | 9 +++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/deeptrack/math.py b/deeptrack/math.py index 3877fdd72..b3525b48e 100644 --- a/deeptrack/math.py +++ b/deeptrack/math.py @@ -102,6 +102,7 @@ class Average(Feature): """Average of input images. + This class computes the average of input images along the specified axis. If `features` is not None, it instead resolves all features in the list and averages the result. @@ -120,10 +121,23 @@ class Average(Feature): Methods ------- - `get(images: np.ndarray, axis: int, **kwargs: dict[str, Any]) --> Image` Computes the average of the input images along the specified axis. + Examples + -------- + Define a simple pipeline with mathematical operations: + >>> import deeptrack as dt + >>> import numpy as np + + >>> input_image1 = np.random.rand(10, 30, 20) + >>> input_image2 = np.random.rand(10, 30, 20) + + >>> average = dt.Average(axis=1) + >>> output_image = average([input_image1, input_image2]) + >>> print(output_image) + (2, 30, 20) + """ __distributed__ = False @@ -134,8 +148,8 @@ def __init__( axis: PropertyLike[int] = 0, **kwargs: dict[str, Any] ): - """Initializes the Average feature. - + """ Initialize the parameters for averaging input features. + This constructor initializes the parameters for averaging input features. diff --git a/deeptrack/tests/test_math.py b/deeptrack/tests/test_math.py index 83a6e4c2b..251c2c458 100644 --- a/deeptrack/tests/test_math.py +++ b/deeptrack/tests/test_math.py @@ -11,6 +11,15 @@ class TestMath(unittest.TestCase): + def test_Average(self): + expected_shape = (10, 30, 20) + input_image0 = np.ones((10, 30, 20)) * 2 + input_image1 = np.ones((10, 30, 20)) * 4 + feature = math.Average(axis=0) + average = feature.resolve([input_image0, input_image1]) + self.assertTrue(np.all(average == 3), True) + self.assertEqual(average.shape, expected_shape) + def test_Clip(self): input_image = np.array([[10, 4], [4, -10]]) feature = math.Clip(min=-5, max=5) From f61f4d8d0150e2ca7eaa186fb9cac84f6b16b70c Mon Sep 17 00:00:00 2001 From: Carlo Date: Wed, 26 Mar 2025 17:55:58 +0100 Subject: [PATCH 07/17] u --- deeptrack/math.py | 116 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 100 insertions(+), 16 deletions(-) diff --git a/deeptrack/math.py b/deeptrack/math.py index b3525b48e..1f016fd60 100644 --- a/deeptrack/math.py +++ b/deeptrack/math.py @@ -121,18 +121,19 @@ class Average(Feature): Methods ------- - `get(images: np.ndarray, axis: int, **kwargs: dict[str, Any]) --> Image` + `get(images: np.ndarray | Image | list[Image], axis: int, **kwargs: dict[str, Any]) --> np.ndarray` Computes the average of the input images along the specified axis. Examples -------- - Define a simple pipeline with mathematical operations: >>> import deeptrack as dt >>> import numpy as np + Create two input images: >>> input_image1 = np.random.rand(10, 30, 20) >>> input_image2 = np.random.rand(10, 30, 20) + Define a simple pipeline with the average feature: >>> average = dt.Average(axis=1) >>> output_image = average([input_image1, input_image2]) >>> print(output_image) @@ -175,7 +176,7 @@ def get( images: np.ndarray | Image | list[Image], axis: int, **kwargs: dict[str, Any], - ) -> Image: + ) -> np.ndarray: """Computes the average of input images along the specified axis. @@ -191,8 +192,8 @@ def get( Returns ------- - Image - The averaged image. + np.ndarray + The average of the input images along the specified axis. """ if self.features is not None: @@ -208,6 +209,9 @@ def get( class Clip(Feature): """Clip the input within a minimum and a maximum value. + This class clips the input values within a specified minimum and maximum + range. + Parameters ---------- min: float @@ -215,17 +219,43 @@ class Clip(Feature): max: float Clip the input to be smaller than this value. + Methods + ------- + `get(image: np.ndarray | Image, min: float, max: float, **kwargs: dict[str, Any]) --> np.ndarray` + Clips the input image within the specified minimum and maximum values. + + Examples + -------- + >>> import deeptrack as dt + >>> import numpy as np + + Create an input image: + >>> input_image = np.array([[10, 4], [4, -10]]) + + Define a clipper feature: + >>> clipper = dt.Clip(min=0, max=5) + >>> output_image = clipper.get(input_image) + >>> print(output_image) + [[5 4] + [4 0]] + """ def __init__( - self, + self: Clip, min: PropertyLike[float] = -np.inf, max: PropertyLike[float] = +np.inf, - **kwargs + **kwargs: dict[str, Any], ): super().__init__(min=min, max=max, **kwargs) - def get(self, image, min=None, max=None, **kwargs): + def get( + self: Clip, + image: np.ndarray | Image, + min: float = None, + max: float = None, + **kwargs: dict[str, Any], + ) -> np.ndarray: return np.clip(image, min, max) @@ -243,19 +273,46 @@ class NormalizeMinMax(Feature): The maximum of the transformation. featurewise: bool Whether to normalize each feature independently. - + + Methods + ------- + `get(image: np.ndarray | Image, min: float, max: float, **kwargs: dict[str, Any]) --> np.ndarray` + Normalizes the input image to be between the specified minimum and + maximum values. + + Examples + -------- + >>> import deeptrack as dt + >>> import numpy as np + + Create an input image: + >>> input_image = np.array([[10, 4], [4, -10]]) + + Define a min-max normalizer: + >>> normalizer = dt.NormalizeMinMax(min=-5, max=5) + >>> output_image = normalizer.get(input_image) + >>> print(output_image) + [[ 5. 2.] + [ 2. -5.]] + """ def __init__( - self, + self: NormalizeMinMax, min: PropertyLike[float] = 0, max: PropertyLike[float] = 1, - featurewise=True, - **kwargs + featurewise: bool = True, + **kwargs: dict[str, Any], ): super().__init__(min=min, max=max, featurewise=featurewise, **kwargs) - def get(self, image, min, max, **kwargs): + def get( + self: NormalizeMinMax, + image: np.ndarray | Image, + min: float = None, + max: float = None, + **kwargs: dict[str, Any], + ) -> np.ndarray: image = image / np.ptp(image) * (max - min) image = image - np.min(image) + min try: @@ -274,13 +331,40 @@ class NormalizeStandard(Feature): ---------- featurewise: bool Whether to normalize each feature independently + + Methods + ------- + `get(image: np.ndarray | Image, **kwargs: dict[str, Any]) --> np.ndarray` + Normalizes (standardizes) the input image to have mean 0 and standard + deviation 1. + + Examples + -------- + >>> import deeptrack as dt + >>> import numpy as np + + Create an input image: + >>> input_image = np.array([[1, 2], [3, 4]], dtype=float) + + >>> standardizer = dt.NormalizeStandard() + >>> output_image = standardizer.get(input_image) + >>> print(output_image) + [[-1.34164079 -0.4472136 0.4472136 1.34164079]] + """ - def __init__(self, featurewise=True, **kwargs): + def __init__( + self:NormalizeStandard, + featurewise: bool = True, + **kwargs: dict[str, Any], + ): super().__init__(featurewise=featurewise, **kwargs) - def get(self, image, **kwargs): - + def get( + self: NormalizeStandard, + image: np.ndarray | Image, + **kwargs: dict[str, Any], + ) -> np.ndarray: return (image - np.mean(image)) / np.std(image) From 9683c920121aa0dfcd223966bb6742ebb8d01551 Mon Sep 17 00:00:00 2001 From: Carlo Date: Wed, 26 Mar 2025 18:15:28 +0100 Subject: [PATCH 08/17] u --- deeptrack/math.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deeptrack/math.py b/deeptrack/math.py index 1f016fd60..2190d74ee 100644 --- a/deeptrack/math.py +++ b/deeptrack/math.py @@ -86,7 +86,7 @@ """ from __future__ import annotations -from typing import Callable, List +from typing import Callable, Any import numpy as np import scipy.ndimage as ndimage From d479aa0f2f8a1cf1de6cbbc83fdb671204eaa620 Mon Sep 17 00:00:00 2001 From: Carlo Date: Wed, 26 Mar 2025 18:28:37 +0100 Subject: [PATCH 09/17] u --- deeptrack/math.py | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/deeptrack/math.py b/deeptrack/math.py index 2190d74ee..90f29f86d 100644 --- a/deeptrack/math.py +++ b/deeptrack/math.py @@ -233,7 +233,7 @@ class Clip(Feature): >>> input_image = np.array([[10, 4], [4, -10]]) Define a clipper feature: - >>> clipper = dt.Clip(min=0, max=5) + >>> clipper = dt.Clip(=0, max=5) >>> output_image = clipper.get(input_image) >>> print(output_image) [[5 4] @@ -347,9 +347,10 @@ class NormalizeStandard(Feature): >>> input_image = np.array([[1, 2], [3, 4]], dtype=float) >>> standardizer = dt.NormalizeStandard() - >>> output_image = standardizer.get(input_image) + >>> output_image = standardizer(input_image) >>> print(output_image) - [[-1.34164079 -0.4472136 0.4472136 1.34164079]] + [[-1.34164079 -0.4472136] + [ 0.4472136 1.34164079]] """ @@ -380,6 +381,26 @@ class NormalizeQuantile(Feature): Quantile range to calculate scaling factor featurewise: bool Whether to normalize each feature independently + + Methods + ------- + `get(image: np.ndarray | Image, quantiles: tuple[float, float], **kwargs: dict[str, Any]) --> np.ndarray` + Normalizes the input image based on the specified quantiles. + + Examples + -------- + >>> import deeptrack as dt + >>> import numpy as np + + Create an input image: + >>> input_image = np.array([[10, 4], [4, -10]]) + + Define a quantile normalizer: + >>> normalizer = dt.NormalizeQuantile(quantiles=(0.25, 0.75)) + >>> output_image = normalizer.get(input_image) + >>> print(output_image) + [[ 1.2 0. ] + [ 0. -2.8]] """ def __init__(self, quantiles=(0.25, 0.75), featurewise=True, **kwargs): From 4bd8b932309278285b1b8329ab32b963e572336d Mon Sep 17 00:00:00 2001 From: Carlo Date: Thu, 27 Mar 2025 16:44:27 +0100 Subject: [PATCH 10/17] u --- deeptrack/math.py | 610 ++++++++++++++++++++++++++++++++--- deeptrack/tests/test_math.py | 41 ++- 2 files changed, 602 insertions(+), 49 deletions(-) diff --git a/deeptrack/math.py b/deeptrack/math.py index 90f29f86d..b075bb13f 100644 --- a/deeptrack/math.py +++ b/deeptrack/math.py @@ -121,7 +121,7 @@ class Average(Feature): Methods ------- - `get(images: np.ndarray | Image | list[Image], axis: int, **kwargs: dict[str, Any]) --> np.ndarray` + `get(images: np.ndarray | Image | list[Image], axis: int, **kwargs: Any) --> np.ndarray` Computes the average of the input images along the specified axis. Examples @@ -139,6 +139,13 @@ class Average(Feature): >>> print(output_image) (2, 30, 20) + Notes + ----- + Calling this feature returns a `np.ndarray` by default. If + `store_properties` is set to `True`, the returned array will be + automatically wrapped in an `Image` object. This behavior is handled + internally and does not affect the return type of the `get()` method. + """ __distributed__ = False @@ -147,9 +154,9 @@ def __init__( self: Average, features: PropertyLike[list[Feature] | None] = None, axis: PropertyLike[int] = 0, - **kwargs: dict[str, Any] + **kwargs: Any ): - """ Initialize the parameters for averaging input features. + """Initialize the parameters for averaging input features. This constructor initializes the parameters for averaging input features. @@ -160,7 +167,7 @@ def __init__( List of features to be resolved and averaged. Defaults to None. axis: int or tuple[int] Axis along which to compute the average. Defaults to 0. - **kwargs: dict[str, Any] + **kwargs: Any Additional keyword arguments. """ @@ -175,7 +182,7 @@ def get( self: Average, images: np.ndarray | Image | list[Image], axis: int, - **kwargs: dict[str, Any], + **kwargs: Any, ) -> np.ndarray: """Computes the average of input images along the specified axis. @@ -221,7 +228,7 @@ class Clip(Feature): Methods ------- - `get(image: np.ndarray | Image, min: float, max: float, **kwargs: dict[str, Any]) --> np.ndarray` + `get(image: np.ndarray | Image, min: float, max: float, **kwargs: Any) --> np.ndarray` Clips the input image within the specified minimum and maximum values. Examples @@ -234,19 +241,41 @@ class Clip(Feature): Define a clipper feature: >>> clipper = dt.Clip(=0, max=5) - >>> output_image = clipper.get(input_image) + >>> output_image = clipper(input_image) >>> print(output_image) [[5 4] [4 0]] + Notes + ----- + Calling this feature returns a `np.ndarray` by default. If + `store_properties` is set to `True`, the returned array will be + automatically wrapped in an `Image` object. This behavior is handled + internally and does not affect the return type of the `get()` method. + """ def __init__( self: Clip, min: PropertyLike[float] = -np.inf, max: PropertyLike[float] = +np.inf, - **kwargs: dict[str, Any], + **kwargs: Any, ): + """Initialize the parameters for clipping input features. + + This constructor initializes the parameters for clipping input features. + + Parameters + ---------- + min: float + Clip the input to be larger than this value. + max: float + Clip the input to be smaller than this value. + **kwargs: Any + Additional keyword arguments. + + """ + super().__init__(min=min, max=max, **kwargs) def get( @@ -254,8 +283,29 @@ def get( image: np.ndarray | Image, min: float = None, max: float = None, - **kwargs: dict[str, Any], + **kwargs: Any, ) -> np.ndarray: + """Clips the input image within the specified minimum and maximum values. + + This method clips the input image within the specified minimum and + maximum values. + + Parameters + ---------- + image: np.ndarray + The input image to clip. + min: float + Clip the input to be larger than this value. + max: float + Clip the input to be smaller than this value. + + Returns + ------- + np.ndarray + The clipped image. + + """ + return np.clip(image, min, max) @@ -276,7 +326,7 @@ class NormalizeMinMax(Feature): Methods ------- - `get(image: np.ndarray | Image, min: float, max: float, **kwargs: dict[str, Any]) --> np.ndarray` + `get(image: np.ndarray | Image, min: float, max: float, **kwargs: Any) --> np.ndarray` Normalizes the input image to be between the specified minimum and maximum values. @@ -290,11 +340,18 @@ class NormalizeMinMax(Feature): Define a min-max normalizer: >>> normalizer = dt.NormalizeMinMax(min=-5, max=5) - >>> output_image = normalizer.get(input_image) + >>> output_image = normalizer(input_image) >>> print(output_image) [[ 5. 2.] [ 2. -5.]] + Notes + ----- + Calling this feature returns a `np.ndarray` by default. If + `store_properties` is set to `True`, the returned array will be + automatically wrapped in an `Image` object. This behavior is handled + internally and does not affect the return type of the `get()` method. + """ def __init__( @@ -302,8 +359,25 @@ def __init__( min: PropertyLike[float] = 0, max: PropertyLike[float] = 1, featurewise: bool = True, - **kwargs: dict[str, Any], + **kwargs: Any, ): + """Initialize the parameters for min-max normalization. + + This constructor initializes the parameters for min-max normalization. + + Parameters + ---------- + min: float + The minimum of the transformation. + max: float + The maximum of the transformation. + featurewise: bool + Whether to normalize each feature independently. + **kwargs: Any + Additional keyword arguments. + + """ + super().__init__(min=min, max=max, featurewise=featurewise, **kwargs) def get( @@ -311,8 +385,30 @@ def get( image: np.ndarray | Image, min: float = None, max: float = None, - **kwargs: dict[str, Any], + **kwargs: Any, ) -> np.ndarray: + """Normalizes the input image to be between the specified minimum and + maximum values. + + This method normalizes the input image to be between the specified + minimum and maximum values. + + Parameters + ---------- + image: np.ndarray + The input image to normalize. + min: float + The minimum of the transformation. + max: float + The maximum of the transformation. + + Returns + ------- + np.ndarray + The normalized image. + + """ + image = image / np.ptp(image) * (max - min) image = image - np.min(image) + min try: @@ -323,9 +419,9 @@ def get( class NormalizeStandard(Feature): - """Image normalization. + """Image normalization (standardization). - Normalize the image to have sigma 1 and mean 0. + Normalize (standardize) the image to have sigma 1 and mean 0. Parameters ---------- @@ -334,10 +430,17 @@ class NormalizeStandard(Feature): Methods ------- - `get(image: np.ndarray | Image, **kwargs: dict[str, Any]) --> np.ndarray` + `get(image: np.ndarray | Image, **kwargs: Any) --> np.ndarray` Normalizes (standardizes) the input image to have mean 0 and standard deviation 1. + Returns + ------- + np.ndarray + The normalized image as a NumPy array. If `_wrap_array_with_image` or + `store_properties` is set to `True`, the result is returned as an `Image` + instead. + Examples -------- >>> import deeptrack as dt @@ -352,20 +455,56 @@ class NormalizeStandard(Feature): [[-1.34164079 -0.4472136] [ 0.4472136 1.34164079]] + Notes + ----- + Calling this feature returns a `np.ndarray` by default. If + `store_properties` is set to `True`, the returned array will be + automatically wrapped in an `Image` object. This behavior is handled + internally and does not affect the return type of the `get()` method. + """ def __init__( self:NormalizeStandard, featurewise: bool = True, - **kwargs: dict[str, Any], + **kwargs: Any, ): + """Initialize the parameters for standardization. + + This constructor initializes the parameters for standardization. + + Parameters + ---------- + featurewise: bool + Whether to normalize each feature independently. + **kwargs: Any + Additional keyword arguments. + + """ super().__init__(featurewise=featurewise, **kwargs) def get( self: NormalizeStandard, image: np.ndarray | Image, - **kwargs: dict[str, Any], + **kwargs: Any, ) -> np.ndarray: + """Normalizes the input image to have mean 0 and standard deviation 1. + + This method normalizes the input image to have mean 0 and standard + deviation 1. + + Parameters + ---------- + image: np.ndarray + The input image to normalize. + + Returns + ------- + np.ndarray + The normalized image. + + """ + return (image - np.mean(image)) / np.std(image) @@ -373,7 +512,7 @@ class NormalizeQuantile(Feature): """Image normalization. Center the image to the median, and divide by the difference between the - quantiles defined by `q_max` and `q_min` + quantiles defined by `q_max` and `q_min`. Parameters ---------- @@ -384,7 +523,7 @@ class NormalizeQuantile(Feature): Methods ------- - `get(image: np.ndarray | Image, quantiles: tuple[float, float], **kwargs: dict[str, Any]) --> np.ndarray` + `get(image: np.ndarray | Image, quantiles: tuple[float, float], **kwargs: Any) --> np.ndarray` Normalizes the input image based on the specified quantiles. Examples @@ -397,20 +536,74 @@ class NormalizeQuantile(Feature): Define a quantile normalizer: >>> normalizer = dt.NormalizeQuantile(quantiles=(0.25, 0.75)) - >>> output_image = normalizer.get(input_image) + >>> output_image = normalizer(input_image) >>> print(output_image) [[ 1.2 0. ] [ 0. -2.8]] + + Notes + ----- + Calling this feature returns a `np.ndarray` by default. If + `store_properties` is set to `True`, the returned array will be + automatically wrapped in an `Image` object. This behavior is handled + internally and does not affect the return type of the `get()` method. + """ - def __init__(self, quantiles=(0.25, 0.75), featurewise=True, **kwargs): + def __init__( + self: NormalizeQuantile, + quantiles: tuple[float, float] = (0.25, 0.75), + featurewise: bool = True, + **kwargs: Any, + ): + """Initialize the parameters for quantile normalization. + + This constructor initializes the parameters for quantile normalization. + + Parameters + ---------- + quantiles: tuple[float, float] + Quantile range to calculate scaling factor. + featurewise: bool + Whether to normalize each feature independently. + **kwargs: Any + Additional keyword arguments. + + """ + super().__init__( self, quantiles=quantiles, featurewise=featurewise, **kwargs) - def get(self, image, quantiles, **kwargs): + def get( + self: NormalizeQuantile, + image: np.ndarray | Image, + quantiles: tuple[float, float] = None, + **kwargs: Any, + ) -> np.ndarray: + """Normalizes the input image based on the specified quantiles. + + This method normalizes the input image based on the specified + quantiles. + + Parameters + ---------- + image: np.ndarray + The input image to normalize. + quantiles: tuple[float, float] + Quantile range to calculate scaling factor. + + Returns + ------- + np.ndarray + The normalized image. + + """ + + if quantiles is None: + quantiles = self.quantiles q_low, q_high, median = np.quantile(image, (*quantiles, 0.5)) return (image - median) / (q_high - q_low) @@ -418,23 +611,109 @@ def get(self, image, quantiles, **kwargs): class Blur(Feature): """Apply a blurring filter to an image. + This class applies a blurring filter to an image. The filter function + must be a function that takes an input image and returns a blurred + image. + Parameters ---------- filter_function: Callable The blurring function to apply. mode: str Border mode for handling boundaries (e.g., 'reflect'). + + Methods + ------- + `get(image: np.ndarray | Image, **kwargs: Any) --> np.ndarray` + Applies the blurring filter to the input image. + + Examples + -------- + >>> import deeptrack as dt + >>> import numpy as np + >>> from scipy.ndimage import convolve + + Create an input image: + >>> input_image = np.random.rand(32, 32) + + Define a Gaussian kernel for blurring + >>> gaussian_kernel = np.array([ + ... [1, 4, 6, 4, 1], + ... [4, 16, 24, 16, 4], + ... [6, 24, 36, 24, 6], + ... [4, 16, 24, 16, 4], + ... [1, 4, 6, 4, 1] + ... ], dtype=float) + >>> gaussian_kernel /= np.sum(gaussian_kernel) + + + Define a blur function using the Gaussian kernel: + >>> def gaussian_blur(input, **kwargs): + ... return convolve(input, gaussian_kernel, mode='reflect') + + Define a blur feature using the Gaussian blur function: + >>> blur = dt.Blur(filter_function=gaussian_blur) + >>> output_image = blur(input_image) + >>> print(output_image.shape) + (32, 32) + + Notes + ----- + Calling this feature returns a `np.ndarray` by default. If + `store_properties` is set to `True`, the returned array will be + automatically wrapped in an `Image` object. This behavior is handled + internally and does not affect the return type of the `get()` method. + """ + def __init__( - self, + self: Blur, filter_function: Callable, mode: PropertyLike[str] = "reflect", - **kwargs + **kwargs: Any, ): + """Initialize the parameters for blurring input features. + + This constructor initializes the parameters for blurring input + features. + + Parameters + ---------- + filter_function: Callable + The blurring function to apply. + mode: str + Border mode for handling boundaries (e.g., 'reflect'). + **kwargs: Any + Additional keyword arguments. + + """ + self.filter = filter_function super().__init__(borderType=mode, **kwargs) - def get(self, image, **kwargs): + def get( + self: Blur, + image: np.ndarray | Image, + **kwargs: Any + ) -> np.ndarray: + """Applies the blurring filter to the input image. + + This method applies the blurring filter to the input image. + + Parameters + ---------- + image: np.ndarray + The input image to blur. + kwargs: dict[str, Any] + Additional keyword arguments. + + Returns + ------- + np.ndarray + The blurred image. + + """ + kwargs.pop("input", False) return utils.safe_call(self.filter, input=image, **kwargs) @@ -449,12 +728,81 @@ class AverageBlur(Blur): ---------- ksize: int Kernel size for the pooling operation. + + Methods + ------- + `get(image: np.ndarray | Image, ksize: int, **kwargs: Any) --> np.ndarray` + Applies the average blurring filter to the input image. + + Examples + -------- + >>> import deeptrack as dt + >>> import numpy as np + + Create an input image: + >>> input_image = np.random.rand(32, 32) + + Define an average blur feature: + >>> average_blur = dt.AverageBlur(ksize=3) + >>> output_image = average_blur(input_image) + >>> print(output_image.shape) + (32, 32) + + Notes + ----- + Calling this feature returns a `np.ndarray` by default. If + `store_properties` is set to `True`, the returned array will be + automatically wrapped in an `Image` object. This behavior is handled + internally and does not affect the return type of the `get()` method. + """ - def __init__(self, ksize: PropertyLike[int] = 3, **kwargs): + def __init__( + self: AverageBlur, + ksize: PropertyLike[int] = 3, + **kwargs: Any, + ): + """Initialize the parameters for averaging input features. + + This constructor initializes the parameters for averaging input + features. + + Parameters + ---------- + ksize: int + Kernel size for the pooling operation. + **kwargs: Any + Additional keyword arguments. + + """ + super().__init__(None, ksize=ksize, **kwargs) - def get(self, input, ksize, **kwargs): + def get( + self: AverageBlur, + input: np.ndarray | Image, + ksize: int, + **kwargs: Any, + ) -> np.ndarray: + """Applies the average blurring filter to the input image. + + This method applies the average blurring filter to the input image. + + Parameters + ---------- + input: np.ndarray + The input image to blur. + ksize: int + Kernel size for the pooling operation. + kwargs: dict[str, Any] + Additional keyword arguments. + + Returns + ------- + np.ndarray + The blurred image. + + """ if input.shape[-1] < ksize: ksize = (ksize,) * (input.ndim - 1) + (1,) @@ -668,70 +1016,236 @@ def get(self, image, dsize, **kwargs): class BlurCV2(Feature): - def __new__(cls, *args, **kwargs): + """Apply a blurring filter using OpenCV2. + + This class applies a blurring filter to an image using OpenCV2. The filter + function must be a function that takes an input image and returns a blurred + image. + + Parameters + ---------- + filter_function: Callable + The blurring function to apply. + mode: str + Border mode for handling boundaries (e.g., 'reflect'). + + Methods + ------- + `get(image: np.ndarray | Image, **kwargs: Any) --> np.ndarray` + Applies the blurring filter to the input image. + + Examples + -------- + >>> import deeptrack as dt + >>> import numpy as np + >>> import cv2 + + Create an input image: + >>> input_image = np.random.rand(32, 32) + + Define a blur feature using the Gaussian blur function: + >>> blur = dt.BlurCV2( + ... filter_function=cv2.GaussianBlur, + ... ksize=(5, 5), + ... sigmaX=1, + ... mode='reflect', + ... ) + >>> output_image = blur(input_image) + >>> print(output_image.shape) + (32, 32) + + Notes + ----- + Calling this feature returns a `np.ndarray` by default. If + `store_properties` is set to `True`, the returned array will be + automatically wrapped in an `Image` object. This behavior is handled + internally and does not affect the return type of the `get()` method. + + """ + + def __new__( + cls: type, + *args: tuple, + **kwargs: Any, + ): + """Ensures that OpenCV (cv2) is available before instantiating the + class. + + This method overrides the default object creation process to perform a + runtime check for the `cv2` module, which is required for this feature. + If OpenCV is not installed, it raises an ImportError with instructions + for installation. + + Parameters + ---------- + *args : tuple + Positional arguments passed to the class constructor. + **kwargs : dict + Keyword arguments passed to the class constructor. + + Returns + ------- + BlurCV2 + An instance of the BlurCV2 feature class. + + Raises + ------ + ImportError + If the OpenCV (`cv2`) module is not available in the current + environment. + + Notes + ----- + This check ensures that users get a clear error message when using the + class without having the required optional dependency installed. + + """ + if not IMPORTED_CV2: raise ImportError( "opencv not installed on device, it is an optional " "dependency of deeptrack. To use this feature, you " "need to install it manually." ) - - return super(BlurCV2, cls).__new__(*args, **kwargs) + return super().__new__(cls) def __init__( - self, + self: BlurCV2, filter_function: Callable, - mode: PropertyLike[str] = "refelct", - **kwargs + mode: PropertyLike[str] = "reflect", + **kwargs: Any, ): + """Initialize the parameters for blurring input features. + + This constructor initializes the parameters for blurring input + features. + + Parameters + ---------- + filter_function: Callable + The blurring function to apply. + mode: str + Border mode for handling boundaries (e.g., 'reflect'). + **kwargs: Any + Additional keyword arguments. + + """ + self.filter = filter_function borderType = _map_mode_to_cv2_borderType[mode] super().__init__(borderType=borderType, **kwargs) - def get(self, image, **kwargs): - kwargs.pop("src", False) - kwargs.pop("dst", False) - utils.safe_call(self.filter, src=image, dst=image, **kwargs) - return image + def get( + self: BlurCV2, + image: np.ndarray | Image, + **kwargs: Any, + ) -> np.ndarray: + """Applies the blurring filter to the input image. + + This method applies the blurring filter to the input image. + + Parameters + ---------- + image: np.ndarray | Image + The input image to be blurred. + **kwargs: Any + Additional parameters for the blurring function. + + Returns + ------- + np.ndarray + The blurred image. + + """ + + kwargs.pop("name", None) + result = self.filter(src=image, **kwargs) + return result + -class BilateralBlur(Blur): +class BilateralBlur(BlurCV2): """Blur an image using a bilateral filter. Bilateral filters blur homogenous areas while trying to preserve edges. - Parameters ---------- d: int Diameter of each pixel neighborhood with value range. - sigma_color: number + sigma_color: float Filter sigma in the color space with value range. A large value of the parameter means that farther colors within the pixel neighborhood (see `sigma_space`) will be mixed together, resulting in larger areas of semi-equal color. - sigma_space: number + sigma_space: float Filter sigma in the coordinate space with value range. A large value of the parameter means that farther pixels will influence each other as long as their colors are close enough (see `sigma_color`). + **kwargs: dict + Additional parameters sent to the blurring function. + + Examples + -------- + >>> import deeptrack as dt + >>> import numpy as np + >>> import cv2 + + Create an input image: + >>> input_image = np.random.rand(32, 32) + + Define a bilateral blur feature: + >>> bilateral_blur = dt.BilateralBlur( + ... d=5, + ... sigma_color=50, + ... sigma_space=50, + ... mode='reflect', + ... ) + >>> output_image = bilateral_blur(input_image) + >>> print(output_image.shape) + (32, 32) + """ def __init__( - self, + self: BilateralBlur, d: PropertyLike[int] = 3, sigma_color: PropertyLike[float] = 50, sigma_space: PropertyLike[float] = 50, - **kwargs + **kwargs: Any, ): + """Initialize the parameters for bilateral blurring. + + This constructor initializes the parameters for bilateral blurring. + + Parameters + ---------- + d: int + Diameter of each pixel neighborhood with value range. + sigma_color: number + Filter sigma in the color space with value range. A + large value of the parameter means that farther colors within the + pixel neighborhood (see `sigma_space`) will be mixed together, + resulting in larger areas of semi-equal color. + sigma_space: number + Filter sigma in the coordinate space with value range. A + large value of the parameter means that farther pixels will influence + each other as long as their colors are close enough (see + `sigma_color`). + **kwargs: dict + Additional parameters sent to the blurring function. + + """ super().__init__( cv2.bilateralFilter, d=d, - sigma_color=sigma_color, - sigma_space=sigma_space, - **kwargs + sigmaColor=sigma_color, + sigmaSpace=sigma_space, + **kwargs, ) + diff --git a/deeptrack/tests/test_math.py b/deeptrack/tests/test_math.py index 251c2c458..4dd39be5d 100644 --- a/deeptrack/tests/test_math.py +++ b/deeptrack/tests/test_math.py @@ -80,7 +80,46 @@ def test_MinPooling(self): feature = math.MinPooling(ksize=2) pooled_image = feature.resolve(input_image) self.assertTrue(np.all(pooled_image == [[1, 3]])) - + + def test_BlurCV2_GaussianBlur(self): + import cv2 + input_image = np.random.rand(32, 32).astype(np.float32) + expected_output = cv2.GaussianBlur(input_image, ksize=(5, 5), sigmaX=1, borderType=cv2.BORDER_REFLECT) + feature = math.BlurCV2(filter_function=cv2.GaussianBlur, ksize=(5, 5), sigmaX=1, mode='reflect') + output_image = feature.resolve(input_image) + self.assertTrue(output_image.shape == expected_output.shape) + self.assertIsNone( + np.testing.assert_allclose( + output_image, expected_output, rtol=1e-5, atol=1e-6, + ) + ) + + def test_BlurCV2_bilateralFilter(self): + import cv2 + input_image = np.random.rand(32, 32).astype(np.float32) + expected_output = cv2.bilateralFilter(input_image, d=9, sigmaColor=75, sigmaSpace=75, borderType=cv2.BORDER_REFLECT) + feature = math.BlurCV2(filter_function=cv2.bilateralFilter, d=9, sigmaColor=75, sigmaSpace=75, mode='reflect') + output_image = feature.resolve(input_image) + self.assertTrue(output_image.shape == expected_output.shape) + self.assertIsNone( + np.testing.assert_allclose( + output_image, expected_output, rtol=1e-5, atol=1e-6, + ) + ) + + def test_BilateralBlur(self): + import cv2 + input_image = np.random.rand(32, 32).astype(np.float32) + expected_output = cv2.bilateralFilter(input_image, d=9, sigmaColor=75, sigmaSpace=75, borderType=cv2.BORDER_REFLECT) + feature = math.BilateralBlur(d=9, sigma_color=75, sigma_space=75, mode='reflect') + output_image = feature.resolve(input_image) + self.assertTrue(output_image.shape == expected_output.shape) + self.assertIsNone( + np.testing.assert_allclose( + output_image, expected_output, rtol=1e-5, atol=1e-6, + ) + ) + if __name__ == "__main__": unittest.main() \ No newline at end of file From 2bf52589d6ecce7f59363171c13ce9bdc9da150a Mon Sep 17 00:00:00 2001 From: Carlo Date: Thu, 27 Mar 2025 17:01:52 +0100 Subject: [PATCH 11/17] u --- deeptrack/tests/test_math.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/deeptrack/tests/test_math.py b/deeptrack/tests/test_math.py index 4dd39be5d..e60c67af3 100644 --- a/deeptrack/tests/test_math.py +++ b/deeptrack/tests/test_math.py @@ -9,6 +9,11 @@ from deeptrack import math +try: + import cv2 + OPENCV_AVAILABLE = True +except ImportError: + OPENCV_AVAILABLE = False class TestMath(unittest.TestCase): def test_Average(self): @@ -81,8 +86,8 @@ def test_MinPooling(self): pooled_image = feature.resolve(input_image) self.assertTrue(np.all(pooled_image == [[1, 3]])) + @unittest.skipUnless(OPENCV_AVAILABLE, "OpenCV is not installed.") def test_BlurCV2_GaussianBlur(self): - import cv2 input_image = np.random.rand(32, 32).astype(np.float32) expected_output = cv2.GaussianBlur(input_image, ksize=(5, 5), sigmaX=1, borderType=cv2.BORDER_REFLECT) feature = math.BlurCV2(filter_function=cv2.GaussianBlur, ksize=(5, 5), sigmaX=1, mode='reflect') @@ -95,7 +100,6 @@ def test_BlurCV2_GaussianBlur(self): ) def test_BlurCV2_bilateralFilter(self): - import cv2 input_image = np.random.rand(32, 32).astype(np.float32) expected_output = cv2.bilateralFilter(input_image, d=9, sigmaColor=75, sigmaSpace=75, borderType=cv2.BORDER_REFLECT) feature = math.BlurCV2(filter_function=cv2.bilateralFilter, d=9, sigmaColor=75, sigmaSpace=75, mode='reflect') @@ -108,7 +112,6 @@ def test_BlurCV2_bilateralFilter(self): ) def test_BilateralBlur(self): - import cv2 input_image = np.random.rand(32, 32).astype(np.float32) expected_output = cv2.bilateralFilter(input_image, d=9, sigmaColor=75, sigmaSpace=75, borderType=cv2.BORDER_REFLECT) feature = math.BilateralBlur(d=9, sigma_color=75, sigma_space=75, mode='reflect') From 35a3e90ee24fe9608433cd14d6e43988a9b4cd2b Mon Sep 17 00:00:00 2001 From: Carlo Date: Thu, 27 Mar 2025 17:05:14 +0100 Subject: [PATCH 12/17] u --- deeptrack/tests/test_math.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/deeptrack/tests/test_math.py b/deeptrack/tests/test_math.py index e60c67af3..4773590f0 100644 --- a/deeptrack/tests/test_math.py +++ b/deeptrack/tests/test_math.py @@ -98,7 +98,7 @@ def test_BlurCV2_GaussianBlur(self): output_image, expected_output, rtol=1e-5, atol=1e-6, ) ) - + @unittest.skipUnless(OPENCV_AVAILABLE, "OpenCV is not installed.") def test_BlurCV2_bilateralFilter(self): input_image = np.random.rand(32, 32).astype(np.float32) expected_output = cv2.bilateralFilter(input_image, d=9, sigmaColor=75, sigmaSpace=75, borderType=cv2.BORDER_REFLECT) @@ -110,7 +110,8 @@ def test_BlurCV2_bilateralFilter(self): output_image, expected_output, rtol=1e-5, atol=1e-6, ) ) - + + @unittest.skipUnless(OPENCV_AVAILABLE, "OpenCV is not installed.") def test_BilateralBlur(self): input_image = np.random.rand(32, 32).astype(np.float32) expected_output = cv2.bilateralFilter(input_image, d=9, sigmaColor=75, sigmaSpace=75, borderType=cv2.BORDER_REFLECT) From 5e752c3a40cc73bbc29aab191c7c4b43306aa33e Mon Sep 17 00:00:00 2001 From: Carlo Date: Mon, 31 Mar 2025 12:06:37 +0200 Subject: [PATCH 13/17] u --- deeptrack/math.py | 203 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 169 insertions(+), 34 deletions(-) diff --git a/deeptrack/math.py b/deeptrack/math.py index b075bb13f..45a6eea75 100644 --- a/deeptrack/math.py +++ b/deeptrack/math.py @@ -636,7 +636,7 @@ class Blur(Feature): Create an input image: >>> input_image = np.random.rand(32, 32) - Define a Gaussian kernel for blurring + Define a Gaussian kernel for blurring: >>> gaussian_kernel = np.array([ ... [1, 4, 6, 4, 1], ... [4, 16, 24, 16, 4], @@ -846,6 +846,8 @@ class MedianBlur(Blur): ---------- ksize: int Kernel size. + kwargs: dict + Additional parameters sent to the blurring function. """ @@ -853,8 +855,6 @@ def __init__(self, ksize: PropertyLike[int] = 3, **kwargs): super().__init__(ndimage.median_filter, k=ksize, **kwargs) -# POOLING - class Pool(Feature): """Downsamples the image by applying a function to local regions of the image. @@ -872,22 +872,61 @@ class Pool(Feature): Examples include np.mean, np.max, np.min, etc. ksize: int Size of the pooling kernel. - cval: number - Value to pad edges with if necessary. - func_kwargs: dict + kwargs: Any Additional parameters sent to the pooling function. """ def __init__( - self, + self: Pool, pooling_function: Callable, ksize: PropertyLike[int] = 3, - **kwargs + **kwargs: Any, ): + """Initialize the parameters for pooling input features. + + This constructor initializes the parameters for pooling input + features. + + Parameters + ---------- + pooling_function: Callable + The pooling function to apply. + ksize: int + Size of the pooling kernel. + **kwargs: Any + Additional keyword arguments. + + """ + self.pooling = pooling_function super().__init__(ksize=ksize, **kwargs) - def get(self, image, ksize, **kwargs): + def get( + self: Pool, + image: np.ndarray | Image, + ksize: int, + **kwargs: Any, + )-> np.ndarray: + """Applies the pooling function to the input image. + + This method applies the pooling function to the input image. + + Parameters + ---------- + image: np.ndarray + The input image to pool. + ksize: int + Size of the pooling kernel. + kwargs: dict[str, Any] + Additional keyword arguments. + + Returns + ------- + np.ndarray + The pooled image. + + """ + kwargs.pop("func", False) kwargs.pop("image", False) kwargs.pop("block_size", False) @@ -907,19 +946,51 @@ class AveragePooling(Pool): ---------- ksize: int Size of the pooling kernel. - cval: number - Value to pad edges with if necessary. Default 0. - func_kwargs: dict + kwargs: dict Additional parameters sent to the pooling function. + + Examples + -------- + >>> import deeptrack as dt + >>> import numpy as np + + Create an input image: + >>> input_image = np.random.rand(32, 32) + + Define an average pooling feature: + >>> average_pooling = dt.AveragePooling(ksize=4) + >>> output_image = average_pooling(input_image) + >>> print(output_image.shape) + (8, 8) + + Notes + ----- + Calling this feature returns a `np.ndarray` by default. If + `store_properties` is set to `True`, the returned array will be + automatically wrapped in an `Image` object. This behavior is handled + internally and does not affect the return type of the `get()` method. + """ - def __init__(self, ksize: PropertyLike[int] = 3, **kwargs): + def __init__( + self: Pool, + ksize: PropertyLike[int] = 3, + **kwargs: Any, + ): super().__init__(np.mean, ksize=ksize, **kwargs) class MaxPooling(Pool): """Apply max pooling to images. + This class reduces the resolution of an image by dividing it into + non-overlapping blocks of size `ksize` and applying the max function to + each block. The result is a downsampled image where each pixel value + represents the maximum value within the corresponding block of the + original image. + This is useful for reducing the size of an image while retaining the + most significant features. + Parameters ---------- ksize: int @@ -928,23 +999,88 @@ class MaxPooling(Pool): Value to pad edges with if necessary. Default 0. func_kwargs: dict Additional parameters sent to the pooling function. + + Examples + -------- + >>> import deeptrack as dt + >>> import numpy as np + Create an input image: + >>> input_image = np.random.rand(32, 32) + + Define a max pooling feature: + >>> max_pooling = dt.MaxPooling(ksize=8) + >>> output_image = max_pooling(input_image) + >>> print(output_image.shape) + (8, 8) + + Notes + ----- + Calling this feature returns a `np.ndarray` by default. If + `store_properties` is set to `True`, the returned array will be + automatically wrapped in an `Image` object. This behavior is handled + internally and does not affect the return type of the `get()` method. + """ - def __init__(self, ksize: PropertyLike[int] = 3, **kwargs): + def __init__( + self: MaxPooling, + ksize: PropertyLike[int] = 3, + , **kwargs, + ): + """Initialize the parameters for max pooling. + + This constructor initializes the parameters for max pooling. + + Parameters + ---------- + ksize: int + Size of the pooling kernel. + **kwargs: Any + Additional keyword arguments. + + """ + super().__init__(np.max, ksize=ksize, **kwargs) class MinPooling(Pool): """Apply min pooling to images. + This class reduces the resolution of an image by dividing it into + non-overlapping blocks of size `ksize` and applying the min function to + each block. The result is a downsampled image where each pixel value + represents the minimum value within the corresponding block of the + original image. + Parameters ---------- ksize: int Size of the pooling kernel. - cval: number - Value to pad edges with if necessary. Default 0. - func_kwargs: dict + kwargs: dict Additional parameters sent to the pooling function. + + Examples + -------- + >>> import deeptrack as dt + >>> import numpy as np + + Create an input image: + >>> input_image = np.random.rand(32, 32) + + Define a min pooling feature: + >>> min_pooling = dt.MinPooling(ksize=3) + >>> output_image = min_pooling(input_image) + >>> print(output_image.shape) + (32, 32) + + Notes + ----- + Calling this feature returns a `np.ndarray` by default. If + `store_properties` is set to `True`, the returned array will be + automatically wrapped in an `Image` object. This behavior is handled + internally and does not affect the return type of the `get()` method. + + """ def __init__(self, ksize: PropertyLike[int] = 3, **kwargs): @@ -997,8 +1133,6 @@ def get(self, image, dsize, **kwargs): ) -# OPENCV2 blur - try: import cv2 @@ -1018,9 +1152,9 @@ def get(self, image, dsize, **kwargs): class BlurCV2(Feature): """Apply a blurring filter using OpenCV2. - This class applies a blurring filter to an image using OpenCV2. The filter - function must be a function that takes an input image and returns a blurred - image. + This class applies a blurring filter to an image using OpenCV2. The + filter_function must be an OpenCV-compatible function that accepts a src + keyword argument (e.g., cv2.GaussianBlur, cv2.bilateralFilter, etc.). Parameters ---------- @@ -1071,10 +1205,9 @@ def __new__( """Ensures that OpenCV (cv2) is available before instantiating the class. - This method overrides the default object creation process to perform a - runtime check for the `cv2` module, which is required for this feature. - If OpenCV is not installed, it raises an ImportError with instructions - for installation. + Overrides the default object creation process to check that the `cv2` + module is available before creating the class. If OpenCV is not + installed, it raises an ImportError with instructions for installation. Parameters ---------- @@ -1093,11 +1226,6 @@ def __new__( ImportError If the OpenCV (`cv2`) module is not available in the current environment. - - Notes - ----- - This check ensures that users get a clear error message when using the - class without having the required optional dependency installed. """ @@ -1147,7 +1275,8 @@ def get( Parameters ---------- image: np.ndarray | Image - The input image to be blurred. + The input image to blur. Can be a NumPy array or DeepTrack Image. + **kwargs: Any Additional parameters for the blurring function. @@ -1161,7 +1290,6 @@ def get( kwargs.pop("name", None) result = self.filter(src=image, **kwargs) return result - class BilateralBlur(BlurCV2): @@ -1208,7 +1336,14 @@ class BilateralBlur(BlurCV2): ... ) >>> output_image = bilateral_blur(input_image) >>> print(output_image.shape) - (32, 32) + (32, 32)] + + Notes + ----- + Calling this feature returns a `np.ndarray` by default. If + `store_properties` is set to `True`, the returned array will be + automatically wrapped in an `Image` object. This behavior is handled + internally and does not affect the return type of the `get()` method. """ From 99686f5f3cf4150045506404ff6ebccb86617ad6 Mon Sep 17 00:00:00 2001 From: Carlo Date: Mon, 31 Mar 2025 12:41:38 +0200 Subject: [PATCH 14/17] u --- deeptrack/math.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deeptrack/math.py b/deeptrack/math.py index 45a6eea75..89e700680 100644 --- a/deeptrack/math.py +++ b/deeptrack/math.py @@ -1025,7 +1025,7 @@ class MaxPooling(Pool): def __init__( self: MaxPooling, ksize: PropertyLike[int] = 3, - , **kwargs, + **kwargs: Any, ): """Initialize the parameters for max pooling. From 1be7845c714e0c8fecf4c70ed500bc934b795d3d Mon Sep 17 00:00:00 2001 From: Carlo Date: Wed, 2 Apr 2025 23:31:31 +0200 Subject: [PATCH 15/17] u --- deeptrack/math.py | 1 - deeptrack/tests/test_math.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/deeptrack/math.py b/deeptrack/math.py index 89e700680..4c171cd1c 100644 --- a/deeptrack/math.py +++ b/deeptrack/math.py @@ -186,7 +186,6 @@ def get( ) -> np.ndarray: """Computes the average of input images along the specified axis. - This method computes the average of the input images along the specified axis. diff --git a/deeptrack/tests/test_math.py b/deeptrack/tests/test_math.py index 4773590f0..1b1da036b 100644 --- a/deeptrack/tests/test_math.py +++ b/deeptrack/tests/test_math.py @@ -110,7 +110,7 @@ def test_BlurCV2_bilateralFilter(self): output_image, expected_output, rtol=1e-5, atol=1e-6, ) ) - + @unittest.skipUnless(OPENCV_AVAILABLE, "OpenCV is not installed.") def test_BilateralBlur(self): input_image = np.random.rand(32, 32).astype(np.float32) From 91257d50b9a256aa208283784f8807ec571ed20f Mon Sep 17 00:00:00 2001 From: Carlo Date: Sun, 6 Apr 2025 11:43:24 +0200 Subject: [PATCH 16/17] u --- deeptrack/math.py | 364 ++++++++++++++++++++++++++++++----- deeptrack/tests/test_math.py | 25 +++ 2 files changed, 339 insertions(+), 50 deletions(-) diff --git a/deeptrack/math.py b/deeptrack/math.py index 4c171cd1c..b7453554c 100644 --- a/deeptrack/math.py +++ b/deeptrack/math.py @@ -3,7 +3,10 @@ This module provides classes and utilities to perform common mathematical operations and transformations on images, including clipping, normalization, blurring, and pooling. These are implemented as subclasses of `Feature` for -seamless integration with the feature-based design of the library. +seamless integration with the feature-based design of the library. Each +`Feature` supports lazy evaluation and can be composed using operators (e.g., +`>>` for chaining), enabling efficient and readable construction of image +processing pipelines. Key Features ------------ @@ -239,7 +242,7 @@ class Clip(Feature): >>> input_image = np.array([[10, 4], [4, -10]]) Define a clipper feature: - >>> clipper = dt.Clip(=0, max=5) + >>> clipper = dt.Clip(min=0, max=5) >>> output_image = clipper(input_image) >>> print(output_image) [[5 4] @@ -284,7 +287,7 @@ def get( max: float = None, **kwargs: Any, ) -> np.ndarray: - """Clips the input image within the specified minimum and maximum values. + """Clips the input image within the specified values. This method clips the input image within the specified minimum and maximum values. @@ -433,13 +436,6 @@ class NormalizeStandard(Feature): Normalizes (standardizes) the input image to have mean 0 and standard deviation 1. - Returns - ------- - np.ndarray - The normalized image as a NumPy array. If `_wrap_array_with_image` or - `store_properties` is set to `True`, the result is returned as an `Image` - instead. - Examples -------- >>> import deeptrack as dt @@ -571,9 +567,8 @@ def __init__( """ super().__init__( - self, - quantiles=quantiles, - featurewise=featurewise, + quantiles=quantiles, + featurewise=featurewise, **kwargs) def get( @@ -617,7 +612,9 @@ class Blur(Feature): Parameters ---------- filter_function: Callable - The blurring function to apply. + The blurring function to apply. This function must accept the input + image as a keyword argument named `input`. If using OpenCV functions + (e.g., `cv2.GaussianBlur`), use `BlurCV2` instead. mode: str Border mode for handling boundaries (e.g., 'reflect'). @@ -662,6 +659,10 @@ class Blur(Feature): `store_properties` is set to `True`, the returned array will be automatically wrapped in an `Image` object. This behavior is handled internally and does not affect the return type of the `get()` method. + The filter_function must accept the input image as a keyword argument named + input. This is required because it is called via utils.safe_call. If you + are using functions that do not support input=... (such as OpenCV filters + like cv2.GaussianBlur), consider using BlurCV2 instead. """ @@ -703,7 +704,7 @@ def get( ---------- image: np.ndarray The input image to blur. - kwargs: dict[str, Any] + **kwargs: dict[str, Any] Additional keyword arguments. Returns @@ -793,7 +794,7 @@ def get( The input image to blur. ksize: int Kernel size for the pooling operation. - kwargs: dict[str, Any] + **kwargs: dict[str, Any] Additional keyword arguments. Returns @@ -819,8 +820,7 @@ def get( class GaussianBlur(Blur): - """Applies a Gaussian blur to images using Gaussian kernels for - image augmentation. + """Applies a Gaussian blur to images using Gaussian kernels. This class blurs images by convolving them with a Gaussian filter, which smooths the image and reduces high-frequency details. The level of blurring @@ -831,27 +831,126 @@ class GaussianBlur(Blur): sigma: float Standard deviation of the Gaussian kernel. + Examples + -------- + >>> import deeptrack as dt + >>> import numpy as np + >>> import matplotlib.pyplot as plt + + Create an input image: + >>> input_image = np.random.rand(32, 32) + + Define a Gaussian blur feature: + >>> gaussian_blur = dt.GaussianBlur(sigma=2) + >>> output_image = gaussian_blur(input_image) + >>> print(output_image.shape) + (32, 32) + + Visualize the input and output images: + >>> plt.figure(figsize=(8, 4)) + >>> plt.subplot(1, 2, 1) + >>> plt.imshow(input_image, cmap='gray') + >>> plt.subplot(1, 2, 2) + >>> plt.imshow(output_image, cmap='gray') + >>> plt.show() + + Notes + ----- + Calling this feature returns a `np.ndarray` by default. If + `store_properties` is set to `True`, the returned array will be + automatically wrapped in an `Image` object. This behavior is handled + internally and does not affect the return type of the `get()` method. + """ - def __init__(self, sigma: PropertyLike[float] = 2, **kwargs): + def __init__( + self: GaussianBlur, + sigma: PropertyLike[float] = 2, + **kwargs: Any + ): + """Initialize the parameters for Gaussian blurring. + + This constructor initializes the parameters for Gaussian blurring. + + Parameters + ---------- + sigma: float + Standard deviation of the Gaussian kernel. + **kwargs: Any + Additional keyword arguments. + + """ + super().__init__(ndimage.gaussian_filter, sigma=sigma, **kwargs) class MedianBlur(Blur): - """Applies a median blur to images by replacing each pixel with the median - of its neighborhood. + """Applies a median blur. + + This class replaces each pixel of the input image with the median value of + its neighborhood. The `ksize` parameter determines the size of the + neighborhood used to calculate the median filter. The median filter is + useful for reducing noise while preserving edges. It is particularly + effective for removing salt-and-pepper noise from images. Parameters ---------- ksize: int Kernel size. - kwargs: dict + **kwargs: dict Additional parameters sent to the blurring function. + Examples + -------- + >>> import deeptrack as dt + >>> import numpy as np + >>> import matplotlib.pyplot as plt + + Create an input image: + >>> input_image = np.random.rand(32, 32) + + Define a median blur feature: + >>> median_blur = dt.MedianBlur(ksize=3) + >>> output_image = median_blur(input_image) + >>> print(output_image.shape) + (32, 32) + + Visualize the input and output images: + >>> plt.figure(figsize=(8, 4)) + >>> plt.subplot(1, 2, 1) + >>> plt.imshow(input_image, cmap='gray') + >>> plt.subplot(1, 2, 2) + >>> plt.imshow(output_image, cmap='gray') + >>> plt.show() + + Notes + ----- + Calling this feature returns a `np.ndarray` by default. If + `store_properties` is set to `True`, the returned array will be + automatically wrapped in an `Image` object. This behavior is handled + internally and does not affect the return type of the `get()` method. + """ - def __init__(self, ksize: PropertyLike[int] = 3, **kwargs): - super().__init__(ndimage.median_filter, k=ksize, **kwargs) + def __init__( + self: MedianBlur, + ksize: PropertyLike[int] = 3, + **kwargs: Any, + ): + """Initialize the parameters for median blurring. + + This constructor initializes the parameters for median blurring. + + Parameters + ---------- + ksize: int + Kernel size. + **kwargs: Any + Additional keyword arguments. + + """ + + super().__init__(ndimage.median_filter, size=ksize, **kwargs) class Pool(Feature): @@ -860,19 +959,53 @@ class Pool(Feature): This class reduces the resolution of an image by dividing it into non-overlapping blocks of size `ksize` and applying the specified pooling - function to each block. + function to each block. The result is a downsampled image where each pixel + value represents the result of the pooling function applied to the + corresponding block. Parameters ---------- pooling_function: function A function that is applied to each local region of the image. - DOES NOT NEED TO BE WRAPPED IN A ANOTHER FUNCTION. - Must support the axis argument. - Examples include np.mean, np.max, np.min, etc. + DOES NOT NEED TO BE WRAPPED IN ANOTHER FUNCTION. + The `pooling_function` must accept the input image as a keyword argument + named `input`, as it is called via `utils.safe_call`. + Examples include `np.mean`, `np.max`, `np.min`, etc. ksize: int Size of the pooling kernel. - kwargs: Any + **kwargs: Any Additional parameters sent to the pooling function. + + Methods + ------- + `get(image: np.ndarray | Image, ksize: int, **kwargs: Any) --> np.ndarray` + Applies the pooling function to the input image. + + Examples + -------- + >>> import deeptrack as dt + >>> import numpy as np + + Create an input image: + >>> input_image = np.random.rand(32, 32) + + Define a pooling feature: + >>> pooling_feature = dt.Pool(pooling_function=np.mean, ksize=4) + >>> output_image = pooling_feature.get(input_image, ksize=4) + >>> print(output_image.shape) + (8, 8) + + Notes + ----- + Calling this feature returns a `np.ndarray` by default. If + `store_properties` is set to `True`, the returned array will be + automatically wrapped in an `Image` object. This behavior is handled + internally and does not affect the return type of the `get()` method. + The filter_function must accept the input image as a keyword argument named + input. This is required because it is called via utils.safe_call. If you + are using functions that do not support input=... (such as OpenCV filters + like cv2.GaussianBlur), consider using BlurCV2 instead. + """ def __init__( @@ -905,7 +1038,7 @@ def get( image: np.ndarray | Image, ksize: int, **kwargs: Any, - )-> np.ndarray: + ) -> np.ndarray: """Applies the pooling function to the input image. This method applies the pooling function to the input image. @@ -916,7 +1049,7 @@ def get( The input image to pool. ksize: int Size of the pooling kernel. - kwargs: dict[str, Any] + **kwargs: dict[str, Any] Additional keyword arguments. Returns @@ -934,18 +1067,24 @@ def get( image=image, func=self.pooling, block_size=ksize, - **kwargs + **kwargs, ) class AveragePooling(Pool): - """Apply average pooling to an images. + """Apply average pooling to an image. + + This class reduces the resolution of an image by dividing it into + non-overlapping blocks of size `ksize` and applying the average function to + each block. The result is a downsampled image where each pixel value + represents the average value within the corresponding block of the + original image. Parameters ---------- ksize: int Size of the pooling kernel. - kwargs: dict + **kwargs: dict Additional parameters sent to the pooling function. Examples @@ -976,6 +1115,19 @@ def __init__( ksize: PropertyLike[int] = 3, **kwargs: Any, ): + """Initialize the parameters for average pooling. + + This constructor initializes the parameters for average pooling. + + Parameters + ---------- + ksize: int + Size of the pooling kernel. + **kwargs: Any + Additional keyword arguments. + + """ + super().__init__(np.mean, ksize=ksize, **kwargs) @@ -1055,7 +1207,7 @@ class MinPooling(Pool): ---------- ksize: int Size of the pooling kernel. - kwargs: dict + **kwargs: dict Additional parameters sent to the pooling function. Examples @@ -1079,48 +1231,164 @@ class MinPooling(Pool): automatically wrapped in an `Image` object. This behavior is handled internally and does not affect the return type of the `get()` method. - """ - def __init__(self, ksize: PropertyLike[int] = 3, **kwargs): + def __init__( + self: MinPooling, + ksize: PropertyLike[int] = 3, + **kwargs: Any, + ): + """Initialize the parameters for min pooling. + + This constructor initializes the parameters for min pooling. + + Parameters + ---------- + ksize: int + Size of the pooling kernel. + **kwargs: Any + Additional keyword arguments. + + """ + super().__init__(np.min, ksize=ksize, **kwargs) class MedianPooling(Pool): """Apply median pooling to images. + This class reduces the resolution of an image by dividing it into + non-overlapping blocks of size `ksize` and applying the median function to + each block. The result is a downsampled image where each pixel value + represents the median value within the corresponding block of the + original image. This is useful for reducing the size of an image while + retaining the most significant features. + Parameters ---------- ksize: int Size of the pooling kernel. - cval: number - Value to pad edges with if necessary. Default 0. - func_kwargs: dict + **kwargs: Any Additional parameters sent to the pooling function. + + Examples + -------- + >>> import deeptrack as dt + >>> import numpy as np + + Create an input image: + >>> input_image = np.random.rand(32, 32) + + Define a median pooling feature: + >>> median_pooling = dt.MedianPooling(ksize=3) + >>> output_image = median_pooling(input_image) + >>> print(output_image.shape) + (32, 32) + + Visualize the input and output images: + >>> plt.figure(figsize=(8, 4)) + >>> plt.subplot(1, 2, 1) + >>> plt.imshow(input_image, cmap='gray') + >>> plt.subplot(1, 2, 2) + >>> plt.imshow(output_image, cmap='gray') + >>> plt.show() + + Notes + ----- + Calling this feature returns a `np.ndarray` by default. If + `store_properties` is set to `True`, the returned array will be + automatically wrapped in an `Image` object. This behavior is handled + internally and does not affect the return type of the `get()` method. + """ - def __init__(self, ksize: PropertyLike[int] = 3, **kwargs): + def __init__( + self: MedianPooling, + ksize: PropertyLike[int] = 3, + **kwargs: Any, + ): + """Initialize the parameters for median pooling. + + This constructor initializes the parameters for median pooling. + + Parameters + ---------- + ksize: int + Size of the pooling kernel. + **kwargs: Any + Additional keyword arguments. + + """ + super().__init__(np.median, ksize=ksize, **kwargs) class Resize(Feature): """Resize an image to a specified size. - This is a wrapper around cv2.resize and takes the same arguments. + This class is a wrapper around cv2.resize and resizes an image to a + specified size. The `dsize` parameter specifies the desired output size of + the image. Note that the order of the axes is different in cv2 and numpy. In cv2, the first axis is the vertical axis, while in numpy it is the horizontal axis. This is reflected in the default values of the arguments. Parameters ---------- - size: tuple + dsize: tuple Size to resize to. + **kwargs: Any + Additional parameters sent to the resizing function. + """ - def __init__(self, dsize: PropertyLike[tuple] = (256, 256), **kwargs): + def __init__( + self: Resize, + dsize: PropertyLike[tuple] = (256, 256), + **kwargs: Any, + ): + """Initialize the parameters for resizing input features. + + This constructor initializes the parameters for resizing input + features. + + Parameters + ---------- + dsize: tuple + Size to resize to. + **kwargs: Any + Additional keyword arguments. + + """ + super().__init__(dsize=dsize, **kwargs) - def get(self, image, dsize, **kwargs): + def get( + self: Resize, + image: np.ndarray, + dsize: tuple, + **kwargs: Any + ) -> np.ndarray: + """Resize the input image to the specified size. + + This method resizes the input image to the specified size. + + Parameters + ---------- + image: np.ndarray + The input image to resize. + dsize: tuple + Desired output size of the image. + **kwargs: Any + Additional keyword arguments. + + Returns + ------- + np.ndarray + The resized image. + + """ + import cv2 from deeptrack import config @@ -1275,7 +1543,6 @@ def get( ---------- image: np.ndarray | Image The input image to blur. Can be a NumPy array or DeepTrack Image. - **kwargs: Any Additional parameters for the blurring function. @@ -1301,19 +1568,16 @@ class BilateralBlur(BlurCV2): ---------- d: int Diameter of each pixel neighborhood with value range. - sigma_color: float Filter sigma in the color space with value range. A large value of the parameter means that farther colors within the pixel neighborhood (see `sigma_space`) will be mixed together, resulting in larger areas of semi-equal color. - sigma_space: float Filter sigma in the coordinate space with value range. A large value of the parameter means that farther pixels will influence each other as long as their colors are close enough (see `sigma_color`). - **kwargs: dict Additional parameters sent to the blurring function. @@ -1335,7 +1599,7 @@ class BilateralBlur(BlurCV2): ... ) >>> output_image = bilateral_blur(input_image) >>> print(output_image.shape) - (32, 32)] + (32, 32) Notes ----- diff --git a/deeptrack/tests/test_math.py b/deeptrack/tests/test_math.py index 1b1da036b..9b84b44dd 100644 --- a/deeptrack/tests/test_math.py +++ b/deeptrack/tests/test_math.py @@ -86,6 +86,31 @@ def test_MinPooling(self): pooled_image = feature.resolve(input_image) self.assertTrue(np.all(pooled_image == [[1, 3]])) + def test_NormalizeQuantile(self): + input_image = np.array([[1, 2], [3, 100]], dtype=float) + feature = math.NormalizeQuantile(quantiles=(0.25, 0.75)) + output = feature.resolve(input_image) + self.assertAlmostEqual(np.median(output), 0, places=5) + + def test_MedianBlur(self): + input_image = np.random.rand(32, 32) + feature = math.MedianBlur(ksize=3) + output = feature.resolve(input_image) + self.assertEqual(output.shape, input_image.shape) + + def test_MedianPooling(self): + input_image = np.array([[1, 3, 2, 4], [5, 7, 6, 8]], dtype=float) + feature = math.MedianPooling(ksize=2) + pooled = feature.resolve(input_image) + self.assertEqual(pooled.shape, (1, 2)) + + def test_Resize(self): + input_image = np.random.rand(16, 16) + feature = math.Resize(dsize=(8, 8)) + resized = feature.resolve(input_image) + self.assertEqual(resized.shape, (8, 8)) + + @unittest.skipUnless(OPENCV_AVAILABLE, "OpenCV is not installed.") def test_BlurCV2_GaussianBlur(self): input_image = np.random.rand(32, 32).astype(np.float32) From 783382011a75e51e1d6ab493069497a0e8ef00ab Mon Sep 17 00:00:00 2001 From: Carlo Date: Sun, 6 Apr 2025 11:45:53 +0200 Subject: [PATCH 17/17] u --- deeptrack/tests/test_math.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deeptrack/tests/test_math.py b/deeptrack/tests/test_math.py index 9b84b44dd..a8a611477 100644 --- a/deeptrack/tests/test_math.py +++ b/deeptrack/tests/test_math.py @@ -104,13 +104,13 @@ def test_MedianPooling(self): pooled = feature.resolve(input_image) self.assertEqual(pooled.shape, (1, 2)) + @unittest.skipUnless(OPENCV_AVAILABLE, "OpenCV is not installed.") def test_Resize(self): input_image = np.random.rand(16, 16) feature = math.Resize(dsize=(8, 8)) resized = feature.resolve(input_image) self.assertEqual(resized.shape, (8, 8)) - @unittest.skipUnless(OPENCV_AVAILABLE, "OpenCV is not installed.") def test_BlurCV2_GaussianBlur(self): input_image = np.random.rand(32, 32).astype(np.float32)