stouputils 1.14.2__py3-none-any.whl → 1.15.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- stouputils/continuous_delivery/pypi.py +1 -1
- stouputils/continuous_delivery/pypi.pyi +3 -2
- stouputils/data_science/config/get.py +51 -51
- stouputils/data_science/data_processing/image/__init__.py +66 -66
- stouputils/data_science/data_processing/image/auto_contrast.py +79 -79
- stouputils/data_science/data_processing/image/axis_flip.py +58 -58
- stouputils/data_science/data_processing/image/bias_field_correction.py +74 -74
- stouputils/data_science/data_processing/image/binary_threshold.py +73 -73
- stouputils/data_science/data_processing/image/blur.py +59 -59
- stouputils/data_science/data_processing/image/brightness.py +54 -54
- stouputils/data_science/data_processing/image/canny.py +110 -110
- stouputils/data_science/data_processing/image/clahe.py +92 -92
- stouputils/data_science/data_processing/image/common.py +30 -30
- stouputils/data_science/data_processing/image/contrast.py +53 -53
- stouputils/data_science/data_processing/image/curvature_flow_filter.py +74 -74
- stouputils/data_science/data_processing/image/denoise.py +378 -378
- stouputils/data_science/data_processing/image/histogram_equalization.py +123 -123
- stouputils/data_science/data_processing/image/invert.py +64 -64
- stouputils/data_science/data_processing/image/laplacian.py +60 -60
- stouputils/data_science/data_processing/image/median_blur.py +52 -52
- stouputils/data_science/data_processing/image/noise.py +59 -59
- stouputils/data_science/data_processing/image/normalize.py +65 -65
- stouputils/data_science/data_processing/image/random_erase.py +66 -66
- stouputils/data_science/data_processing/image/resize.py +69 -69
- stouputils/data_science/data_processing/image/rotation.py +80 -80
- stouputils/data_science/data_processing/image/salt_pepper.py +68 -68
- stouputils/data_science/data_processing/image/sharpening.py +55 -55
- stouputils/data_science/data_processing/image/shearing.py +64 -64
- stouputils/data_science/data_processing/image/threshold.py +64 -64
- stouputils/data_science/data_processing/image/translation.py +71 -71
- stouputils/data_science/data_processing/image/zoom.py +83 -83
- stouputils/data_science/data_processing/image_augmentation.py +118 -118
- stouputils/data_science/data_processing/image_preprocess.py +183 -183
- stouputils/data_science/data_processing/prosthesis_detection.py +359 -359
- stouputils/data_science/data_processing/technique.py +481 -481
- stouputils/data_science/dataset/__init__.py +45 -45
- stouputils/data_science/dataset/dataset.py +292 -292
- stouputils/data_science/dataset/dataset_loader.py +135 -135
- stouputils/data_science/dataset/grouping_strategy.py +296 -296
- stouputils/data_science/dataset/image_loader.py +100 -100
- stouputils/data_science/dataset/xy_tuple.py +696 -696
- stouputils/data_science/metric_dictionnary.py +106 -106
- stouputils/data_science/mlflow_utils.py +206 -206
- stouputils/data_science/models/abstract_model.py +149 -149
- stouputils/data_science/models/all.py +85 -85
- stouputils/data_science/models/keras/all.py +38 -38
- stouputils/data_science/models/keras/convnext.py +62 -62
- stouputils/data_science/models/keras/densenet.py +50 -50
- stouputils/data_science/models/keras/efficientnet.py +60 -60
- stouputils/data_science/models/keras/mobilenet.py +56 -56
- stouputils/data_science/models/keras/resnet.py +52 -52
- stouputils/data_science/models/keras/squeezenet.py +233 -233
- stouputils/data_science/models/keras/vgg.py +42 -42
- stouputils/data_science/models/keras/xception.py +38 -38
- stouputils/data_science/models/keras_utils/callbacks/__init__.py +20 -20
- stouputils/data_science/models/keras_utils/callbacks/colored_progress_bar.py +219 -219
- stouputils/data_science/models/keras_utils/callbacks/learning_rate_finder.py +148 -148
- stouputils/data_science/models/keras_utils/callbacks/model_checkpoint_v2.py +31 -31
- stouputils/data_science/models/keras_utils/callbacks/progressive_unfreezing.py +249 -249
- stouputils/data_science/models/keras_utils/callbacks/warmup_scheduler.py +66 -66
- stouputils/data_science/models/keras_utils/losses/__init__.py +12 -12
- stouputils/data_science/models/keras_utils/losses/next_generation_loss.py +56 -56
- stouputils/data_science/models/keras_utils/visualizations.py +416 -416
- stouputils/data_science/models/sandbox.py +116 -116
- stouputils/data_science/range_tuple.py +234 -234
- stouputils/data_science/utils.py +285 -285
- stouputils/decorators.py +53 -39
- stouputils/decorators.pyi +12 -2
- stouputils/installer/__init__.py +18 -18
- stouputils/installer/linux.py +144 -144
- stouputils/installer/main.py +223 -223
- stouputils/installer/windows.py +136 -136
- stouputils/io.py +16 -9
- stouputils/parallel.pyi +12 -7
- stouputils/print.py +229 -2
- stouputils/print.pyi +92 -3
- stouputils/py.typed +1 -1
- {stouputils-1.14.2.dist-info → stouputils-1.15.0.dist-info}/METADATA +1 -1
- stouputils-1.15.0.dist-info/RECORD +140 -0
- {stouputils-1.14.2.dist-info → stouputils-1.15.0.dist-info}/WHEEL +1 -1
- stouputils/stouputils/__init__.pyi +0 -15
- stouputils/stouputils/_deprecated.pyi +0 -12
- stouputils/stouputils/all_doctests.pyi +0 -46
- stouputils/stouputils/applications/__init__.pyi +0 -2
- stouputils/stouputils/applications/automatic_docs.pyi +0 -106
- stouputils/stouputils/applications/upscaler/__init__.pyi +0 -3
- stouputils/stouputils/applications/upscaler/config.pyi +0 -18
- stouputils/stouputils/applications/upscaler/image.pyi +0 -109
- stouputils/stouputils/applications/upscaler/video.pyi +0 -60
- stouputils/stouputils/archive.pyi +0 -67
- stouputils/stouputils/backup.pyi +0 -109
- stouputils/stouputils/collections.pyi +0 -86
- stouputils/stouputils/continuous_delivery/__init__.pyi +0 -5
- stouputils/stouputils/continuous_delivery/cd_utils.pyi +0 -129
- stouputils/stouputils/continuous_delivery/github.pyi +0 -162
- stouputils/stouputils/continuous_delivery/pypi.pyi +0 -53
- stouputils/stouputils/continuous_delivery/pyproject.pyi +0 -67
- stouputils/stouputils/continuous_delivery/stubs.pyi +0 -39
- stouputils/stouputils/ctx.pyi +0 -211
- stouputils/stouputils/decorators.pyi +0 -252
- stouputils/stouputils/image.pyi +0 -172
- stouputils/stouputils/installer/__init__.pyi +0 -5
- stouputils/stouputils/installer/common.pyi +0 -39
- stouputils/stouputils/installer/downloader.pyi +0 -24
- stouputils/stouputils/installer/linux.pyi +0 -39
- stouputils/stouputils/installer/main.pyi +0 -57
- stouputils/stouputils/installer/windows.pyi +0 -31
- stouputils/stouputils/io.pyi +0 -213
- stouputils/stouputils/parallel.pyi +0 -216
- stouputils/stouputils/print.pyi +0 -136
- stouputils/stouputils/version_pkg.pyi +0 -15
- stouputils-1.14.2.dist-info/RECORD +0 -171
- {stouputils-1.14.2.dist-info → stouputils-1.15.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
|
|
2
|
-
# pyright: reportUnknownMemberType=false
|
|
3
|
-
# pyright: reportUnknownArgumentType=false
|
|
4
|
-
# pyright: reportUnknownVariableType=false
|
|
5
|
-
|
|
6
|
-
# Imports
|
|
7
|
-
import SimpleITK as Sitk
|
|
8
|
-
|
|
9
|
-
from .common import Any, NDArray, check_image, np
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
# Functions
|
|
13
|
-
def bias_field_correction_image(image: NDArray[Any], ignore_dtype: bool = False) -> NDArray[Any]:
|
|
14
|
-
""" Apply a bias field correction to an image. (N4 Filter)
|
|
15
|
-
|
|
16
|
-
Args:
|
|
17
|
-
image (NDArray[Any]): Image to apply the bias field correction (can't be 8-bit unsigned integer)
|
|
18
|
-
ignore_dtype (bool): Ignore the dtype check
|
|
19
|
-
Returns:
|
|
20
|
-
NDArray[Any]: Image with the curvature flow filter applied
|
|
21
|
-
|
|
22
|
-
>>> ## Basic tests
|
|
23
|
-
>>> image = np.random.randint(0, 255, size=(10,10), dtype=np.uint8) / 255
|
|
24
|
-
>>> corrected = bias_field_correction_image(image)
|
|
25
|
-
>>> corrected.shape == image.shape
|
|
26
|
-
True
|
|
27
|
-
>>> corrected.dtype == np.float64
|
|
28
|
-
True
|
|
29
|
-
|
|
30
|
-
>>> ## Test invalid inputs
|
|
31
|
-
>>> bias_field_correction_image("not an image")
|
|
32
|
-
Traceback (most recent call last):
|
|
33
|
-
...
|
|
34
|
-
AssertionError: Image must be a numpy array
|
|
35
|
-
"""
|
|
36
|
-
# Check input data
|
|
37
|
-
check_image(image, ignore_dtype=ignore_dtype)
|
|
38
|
-
|
|
39
|
-
# If the image is 3D, convert to grayscale first
|
|
40
|
-
if image.ndim == 3:
|
|
41
|
-
image = np.mean(image, axis=-1)
|
|
42
|
-
|
|
43
|
-
# Convert numpy array to SimpleITK image
|
|
44
|
-
image_sitk: Sitk.Image = Sitk.GetImageFromArray(image)
|
|
45
|
-
|
|
46
|
-
# Create binary mask of the head region
|
|
47
|
-
transformed: Sitk.Image = Sitk.RescaleIntensity(image_sitk) # Normalize intensities
|
|
48
|
-
transformed = Sitk.LiThreshold(transformed, 0, 1) # Apply Li thresholding
|
|
49
|
-
head_mask: Sitk.Image = transformed
|
|
50
|
-
|
|
51
|
-
# Downsample images to speed up bias field estimation
|
|
52
|
-
shrink_factor: int = 4 # Reduce image size by factor of 4
|
|
53
|
-
input_image: Sitk.Image = Sitk.Shrink(
|
|
54
|
-
image_sitk,
|
|
55
|
-
[shrink_factor] * image_sitk.GetDimension() # Apply shrink factor to all dimensions
|
|
56
|
-
)
|
|
57
|
-
mask_image: Sitk.Image = Sitk.Shrink(
|
|
58
|
-
head_mask,
|
|
59
|
-
[shrink_factor] * image_sitk.GetDimension()
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
# Apply N4 bias field correction
|
|
63
|
-
corrector = Sitk.N4BiasFieldCorrectionImageFilter()
|
|
64
|
-
corrector.Execute(input_image, mask_image)
|
|
65
|
-
|
|
66
|
-
# Get estimated bias field and apply correction
|
|
67
|
-
log_bias_field: Sitk.Image = Sitk.Cast(
|
|
68
|
-
corrector.GetLogBiasFieldAsImage(image_sitk), Sitk.sitkFloat64
|
|
69
|
-
)
|
|
70
|
-
corrected_image_full_resolution: Sitk.Image = image_sitk / Sitk.Exp(log_bias_field)
|
|
71
|
-
|
|
72
|
-
# Convert back to numpy array and return
|
|
73
|
-
return Sitk.GetArrayFromImage(corrected_image_full_resolution)
|
|
74
|
-
|
|
1
|
+
|
|
2
|
+
# pyright: reportUnknownMemberType=false
|
|
3
|
+
# pyright: reportUnknownArgumentType=false
|
|
4
|
+
# pyright: reportUnknownVariableType=false
|
|
5
|
+
|
|
6
|
+
# Imports
|
|
7
|
+
import SimpleITK as Sitk
|
|
8
|
+
|
|
9
|
+
from .common import Any, NDArray, check_image, np
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Functions
|
|
13
|
+
def bias_field_correction_image(image: NDArray[Any], ignore_dtype: bool = False) -> NDArray[Any]:
|
|
14
|
+
""" Apply a bias field correction to an image. (N4 Filter)
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
image (NDArray[Any]): Image to apply the bias field correction (can't be 8-bit unsigned integer)
|
|
18
|
+
ignore_dtype (bool): Ignore the dtype check
|
|
19
|
+
Returns:
|
|
20
|
+
NDArray[Any]: Image with the curvature flow filter applied
|
|
21
|
+
|
|
22
|
+
>>> ## Basic tests
|
|
23
|
+
>>> image = np.random.randint(0, 255, size=(10,10), dtype=np.uint8) / 255
|
|
24
|
+
>>> corrected = bias_field_correction_image(image)
|
|
25
|
+
>>> corrected.shape == image.shape
|
|
26
|
+
True
|
|
27
|
+
>>> corrected.dtype == np.float64
|
|
28
|
+
True
|
|
29
|
+
|
|
30
|
+
>>> ## Test invalid inputs
|
|
31
|
+
>>> bias_field_correction_image("not an image")
|
|
32
|
+
Traceback (most recent call last):
|
|
33
|
+
...
|
|
34
|
+
AssertionError: Image must be a numpy array
|
|
35
|
+
"""
|
|
36
|
+
# Check input data
|
|
37
|
+
check_image(image, ignore_dtype=ignore_dtype)
|
|
38
|
+
|
|
39
|
+
# If the image is 3D, convert to grayscale first
|
|
40
|
+
if image.ndim == 3:
|
|
41
|
+
image = np.mean(image, axis=-1)
|
|
42
|
+
|
|
43
|
+
# Convert numpy array to SimpleITK image
|
|
44
|
+
image_sitk: Sitk.Image = Sitk.GetImageFromArray(image)
|
|
45
|
+
|
|
46
|
+
# Create binary mask of the head region
|
|
47
|
+
transformed: Sitk.Image = Sitk.RescaleIntensity(image_sitk) # Normalize intensities
|
|
48
|
+
transformed = Sitk.LiThreshold(transformed, 0, 1) # Apply Li thresholding
|
|
49
|
+
head_mask: Sitk.Image = transformed
|
|
50
|
+
|
|
51
|
+
# Downsample images to speed up bias field estimation
|
|
52
|
+
shrink_factor: int = 4 # Reduce image size by factor of 4
|
|
53
|
+
input_image: Sitk.Image = Sitk.Shrink(
|
|
54
|
+
image_sitk,
|
|
55
|
+
[shrink_factor] * image_sitk.GetDimension() # Apply shrink factor to all dimensions
|
|
56
|
+
)
|
|
57
|
+
mask_image: Sitk.Image = Sitk.Shrink(
|
|
58
|
+
head_mask,
|
|
59
|
+
[shrink_factor] * image_sitk.GetDimension()
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Apply N4 bias field correction
|
|
63
|
+
corrector = Sitk.N4BiasFieldCorrectionImageFilter()
|
|
64
|
+
corrector.Execute(input_image, mask_image)
|
|
65
|
+
|
|
66
|
+
# Get estimated bias field and apply correction
|
|
67
|
+
log_bias_field: Sitk.Image = Sitk.Cast(
|
|
68
|
+
corrector.GetLogBiasFieldAsImage(image_sitk), Sitk.sitkFloat64
|
|
69
|
+
)
|
|
70
|
+
corrected_image_full_resolution: Sitk.Image = image_sitk / Sitk.Exp(log_bias_field)
|
|
71
|
+
|
|
72
|
+
# Convert back to numpy array and return
|
|
73
|
+
return Sitk.GetArrayFromImage(corrected_image_full_resolution)
|
|
74
|
+
|
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
|
|
2
|
-
# pyright: reportUnusedImport=false
|
|
3
|
-
# ruff: noqa: F401
|
|
4
|
-
|
|
5
|
-
# Imports
|
|
6
|
-
from .common import Any, NDArray, check_image, cv2, np
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
# Functions
|
|
10
|
-
def binary_threshold_image(image: NDArray[Any], threshold: float, ignore_dtype: bool = False) -> NDArray[Any]:
|
|
11
|
-
""" Apply binary threshold to an image.
|
|
12
|
-
|
|
13
|
-
Args:
|
|
14
|
-
image (NDArray[Any]): Image to threshold
|
|
15
|
-
threshold (float): Threshold value (between 0 and 1)
|
|
16
|
-
ignore_dtype (bool): Ignore the dtype check
|
|
17
|
-
Returns:
|
|
18
|
-
NDArray[Any]: Thresholded binary image
|
|
19
|
-
|
|
20
|
-
>>> ## Basic tests
|
|
21
|
-
>>> image = np.array([[100, 150, 200], [50, 125, 175], [25, 75, 225]])
|
|
22
|
-
>>> binary_threshold_image(image.astype(np.uint8), 0.5).tolist()
|
|
23
|
-
[[0, 255, 255], [0, 0, 255], [0, 0, 255]]
|
|
24
|
-
|
|
25
|
-
>>> np.random.seed(42)
|
|
26
|
-
>>> img = np.random.randint(0, 256, (4,4), dtype=np.uint8)
|
|
27
|
-
>>> thresholded = binary_threshold_image(img, 0.7)
|
|
28
|
-
>>> set(np.unique(thresholded).tolist()) <= {0, 255} # Should only contain 0 and 255
|
|
29
|
-
True
|
|
30
|
-
|
|
31
|
-
>>> rgb = np.random.randint(0, 256, (3,3,3), dtype=np.uint8)
|
|
32
|
-
>>> thresh_rgb = binary_threshold_image(rgb, 0.5)
|
|
33
|
-
>>> thresh_rgb.shape == rgb.shape
|
|
34
|
-
True
|
|
35
|
-
>>> set(np.unique(thresh_rgb).tolist()) <= {0, 255}
|
|
36
|
-
True
|
|
37
|
-
|
|
38
|
-
>>> ## Test invalid inputs
|
|
39
|
-
>>> binary_threshold_image("not an image", 0.5)
|
|
40
|
-
Traceback (most recent call last):
|
|
41
|
-
...
|
|
42
|
-
AssertionError: Image must be a numpy array
|
|
43
|
-
|
|
44
|
-
>>> binary_threshold_image(image.astype(np.uint8), "0.5")
|
|
45
|
-
Traceback (most recent call last):
|
|
46
|
-
...
|
|
47
|
-
AssertionError: threshold must be a number, got <class 'str'>
|
|
48
|
-
|
|
49
|
-
>>> binary_threshold_image(image.astype(np.uint8), 1.5)
|
|
50
|
-
Traceback (most recent call last):
|
|
51
|
-
...
|
|
52
|
-
AssertionError: threshold must be between 0 and 1, got 1.5
|
|
53
|
-
"""
|
|
54
|
-
# Check input data
|
|
55
|
-
check_image(image, ignore_dtype=ignore_dtype)
|
|
56
|
-
assert isinstance(threshold, float | int), f"threshold must be a number, got {type(threshold)}"
|
|
57
|
-
assert 0 <= threshold <= 1, f"threshold must be between 0 and 1, got {threshold}"
|
|
58
|
-
|
|
59
|
-
# Convert threshold from 0-1 range to 0-255 range
|
|
60
|
-
threshold_value: int = int(threshold * 255)
|
|
61
|
-
|
|
62
|
-
# Apply threshold
|
|
63
|
-
if len(image.shape) == 2:
|
|
64
|
-
# Grayscale image
|
|
65
|
-
binary: NDArray[Any] = cv2.threshold(image, threshold_value, 255, cv2.THRESH_BINARY)[1]
|
|
66
|
-
else:
|
|
67
|
-
# Color image - convert to grayscale first, then back to color
|
|
68
|
-
gray: NDArray[Any] = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
|
69
|
-
binary: NDArray[Any] = cv2.threshold(gray, threshold_value, 255, cv2.THRESH_BINARY)[1]
|
|
70
|
-
binary = cv2.cvtColor(binary, cv2.COLOR_GRAY2BGR)
|
|
71
|
-
|
|
72
|
-
return binary
|
|
73
|
-
|
|
1
|
+
|
|
2
|
+
# pyright: reportUnusedImport=false
|
|
3
|
+
# ruff: noqa: F401
|
|
4
|
+
|
|
5
|
+
# Imports
|
|
6
|
+
from .common import Any, NDArray, check_image, cv2, np
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# Functions
|
|
10
|
+
def binary_threshold_image(image: NDArray[Any], threshold: float, ignore_dtype: bool = False) -> NDArray[Any]:
|
|
11
|
+
""" Apply binary threshold to an image.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
image (NDArray[Any]): Image to threshold
|
|
15
|
+
threshold (float): Threshold value (between 0 and 1)
|
|
16
|
+
ignore_dtype (bool): Ignore the dtype check
|
|
17
|
+
Returns:
|
|
18
|
+
NDArray[Any]: Thresholded binary image
|
|
19
|
+
|
|
20
|
+
>>> ## Basic tests
|
|
21
|
+
>>> image = np.array([[100, 150, 200], [50, 125, 175], [25, 75, 225]])
|
|
22
|
+
>>> binary_threshold_image(image.astype(np.uint8), 0.5).tolist()
|
|
23
|
+
[[0, 255, 255], [0, 0, 255], [0, 0, 255]]
|
|
24
|
+
|
|
25
|
+
>>> np.random.seed(42)
|
|
26
|
+
>>> img = np.random.randint(0, 256, (4,4), dtype=np.uint8)
|
|
27
|
+
>>> thresholded = binary_threshold_image(img, 0.7)
|
|
28
|
+
>>> set(np.unique(thresholded).tolist()) <= {0, 255} # Should only contain 0 and 255
|
|
29
|
+
True
|
|
30
|
+
|
|
31
|
+
>>> rgb = np.random.randint(0, 256, (3,3,3), dtype=np.uint8)
|
|
32
|
+
>>> thresh_rgb = binary_threshold_image(rgb, 0.5)
|
|
33
|
+
>>> thresh_rgb.shape == rgb.shape
|
|
34
|
+
True
|
|
35
|
+
>>> set(np.unique(thresh_rgb).tolist()) <= {0, 255}
|
|
36
|
+
True
|
|
37
|
+
|
|
38
|
+
>>> ## Test invalid inputs
|
|
39
|
+
>>> binary_threshold_image("not an image", 0.5)
|
|
40
|
+
Traceback (most recent call last):
|
|
41
|
+
...
|
|
42
|
+
AssertionError: Image must be a numpy array
|
|
43
|
+
|
|
44
|
+
>>> binary_threshold_image(image.astype(np.uint8), "0.5")
|
|
45
|
+
Traceback (most recent call last):
|
|
46
|
+
...
|
|
47
|
+
AssertionError: threshold must be a number, got <class 'str'>
|
|
48
|
+
|
|
49
|
+
>>> binary_threshold_image(image.astype(np.uint8), 1.5)
|
|
50
|
+
Traceback (most recent call last):
|
|
51
|
+
...
|
|
52
|
+
AssertionError: threshold must be between 0 and 1, got 1.5
|
|
53
|
+
"""
|
|
54
|
+
# Check input data
|
|
55
|
+
check_image(image, ignore_dtype=ignore_dtype)
|
|
56
|
+
assert isinstance(threshold, float | int), f"threshold must be a number, got {type(threshold)}"
|
|
57
|
+
assert 0 <= threshold <= 1, f"threshold must be between 0 and 1, got {threshold}"
|
|
58
|
+
|
|
59
|
+
# Convert threshold from 0-1 range to 0-255 range
|
|
60
|
+
threshold_value: int = int(threshold * 255)
|
|
61
|
+
|
|
62
|
+
# Apply threshold
|
|
63
|
+
if len(image.shape) == 2:
|
|
64
|
+
# Grayscale image
|
|
65
|
+
binary: NDArray[Any] = cv2.threshold(image, threshold_value, 255, cv2.THRESH_BINARY)[1]
|
|
66
|
+
else:
|
|
67
|
+
# Color image - convert to grayscale first, then back to color
|
|
68
|
+
gray: NDArray[Any] = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
|
69
|
+
binary: NDArray[Any] = cv2.threshold(gray, threshold_value, 255, cv2.THRESH_BINARY)[1]
|
|
70
|
+
binary = cv2.cvtColor(binary, cv2.COLOR_GRAY2BGR)
|
|
71
|
+
|
|
72
|
+
return binary
|
|
73
|
+
|
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
|
|
2
|
-
# pyright: reportUnusedImport=false
|
|
3
|
-
# ruff: noqa: F401
|
|
4
|
-
|
|
5
|
-
# Imports
|
|
6
|
-
from .common import Any, NDArray, check_image, cv2, np
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
# Functions
|
|
10
|
-
def blur_image(image: NDArray[Any], blur_strength: float, ignore_dtype: bool = False) -> NDArray[Any]:
|
|
11
|
-
""" Apply Gaussian blur to an image.
|
|
12
|
-
|
|
13
|
-
Args:
|
|
14
|
-
image (NDArray[Any]): Image to blur
|
|
15
|
-
blur_strength (float): Strength of the blur
|
|
16
|
-
ignore_dtype (bool): Ignore the dtype check
|
|
17
|
-
Returns:
|
|
18
|
-
NDArray[Any]: Blurred image
|
|
19
|
-
|
|
20
|
-
>>> ## Basic tests
|
|
21
|
-
>>> image = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
|
|
22
|
-
>>> blurred = blur_image(image.astype(np.uint8), 1.5)
|
|
23
|
-
>>> blurred.shape == image.shape
|
|
24
|
-
True
|
|
25
|
-
|
|
26
|
-
>>> img = np.zeros((5,5), dtype=np.uint8)
|
|
27
|
-
>>> img[2,2] = 255 # Single bright pixel
|
|
28
|
-
>>> blurred = blur_image(img, 1.0)
|
|
29
|
-
>>> bool(blurred[2,2] < 255) # Center should be blurred
|
|
30
|
-
True
|
|
31
|
-
|
|
32
|
-
>>> rgb = np.full((3,3,3), 128, dtype=np.uint8)
|
|
33
|
-
>>> blurred_rgb = blur_image(rgb, 1.0)
|
|
34
|
-
>>> blurred_rgb.shape == (3,3,3)
|
|
35
|
-
True
|
|
36
|
-
|
|
37
|
-
>>> ## Test invalid inputs
|
|
38
|
-
>>> blur_image("not an image", 1.5)
|
|
39
|
-
Traceback (most recent call last):
|
|
40
|
-
...
|
|
41
|
-
AssertionError: Image must be a numpy array
|
|
42
|
-
|
|
43
|
-
>>> blur_image(image.astype(np.uint8), "1.5")
|
|
44
|
-
Traceback (most recent call last):
|
|
45
|
-
...
|
|
46
|
-
AssertionError: blur_strength must be a number, got <class 'str'>
|
|
47
|
-
"""
|
|
48
|
-
# Check input data
|
|
49
|
-
check_image(image, ignore_dtype=ignore_dtype)
|
|
50
|
-
assert isinstance(blur_strength, float | int), f"blur_strength must be a number, got {type(blur_strength)}"
|
|
51
|
-
|
|
52
|
-
# Apply Gaussian blur
|
|
53
|
-
kernel_size: int = max(3, int(blur_strength * 2) + 1)
|
|
54
|
-
if kernel_size % 2 == 0:
|
|
55
|
-
kernel_size += 1
|
|
56
|
-
blurred_image: NDArray[Any] = cv2.GaussianBlur(image, (kernel_size, kernel_size), 0)
|
|
57
|
-
|
|
58
|
-
return blurred_image
|
|
59
|
-
|
|
1
|
+
|
|
2
|
+
# pyright: reportUnusedImport=false
|
|
3
|
+
# ruff: noqa: F401
|
|
4
|
+
|
|
5
|
+
# Imports
|
|
6
|
+
from .common import Any, NDArray, check_image, cv2, np
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# Functions
|
|
10
|
+
def blur_image(image: NDArray[Any], blur_strength: float, ignore_dtype: bool = False) -> NDArray[Any]:
|
|
11
|
+
""" Apply Gaussian blur to an image.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
image (NDArray[Any]): Image to blur
|
|
15
|
+
blur_strength (float): Strength of the blur
|
|
16
|
+
ignore_dtype (bool): Ignore the dtype check
|
|
17
|
+
Returns:
|
|
18
|
+
NDArray[Any]: Blurred image
|
|
19
|
+
|
|
20
|
+
>>> ## Basic tests
|
|
21
|
+
>>> image = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
|
|
22
|
+
>>> blurred = blur_image(image.astype(np.uint8), 1.5)
|
|
23
|
+
>>> blurred.shape == image.shape
|
|
24
|
+
True
|
|
25
|
+
|
|
26
|
+
>>> img = np.zeros((5,5), dtype=np.uint8)
|
|
27
|
+
>>> img[2,2] = 255 # Single bright pixel
|
|
28
|
+
>>> blurred = blur_image(img, 1.0)
|
|
29
|
+
>>> bool(blurred[2,2] < 255) # Center should be blurred
|
|
30
|
+
True
|
|
31
|
+
|
|
32
|
+
>>> rgb = np.full((3,3,3), 128, dtype=np.uint8)
|
|
33
|
+
>>> blurred_rgb = blur_image(rgb, 1.0)
|
|
34
|
+
>>> blurred_rgb.shape == (3,3,3)
|
|
35
|
+
True
|
|
36
|
+
|
|
37
|
+
>>> ## Test invalid inputs
|
|
38
|
+
>>> blur_image("not an image", 1.5)
|
|
39
|
+
Traceback (most recent call last):
|
|
40
|
+
...
|
|
41
|
+
AssertionError: Image must be a numpy array
|
|
42
|
+
|
|
43
|
+
>>> blur_image(image.astype(np.uint8), "1.5")
|
|
44
|
+
Traceback (most recent call last):
|
|
45
|
+
...
|
|
46
|
+
AssertionError: blur_strength must be a number, got <class 'str'>
|
|
47
|
+
"""
|
|
48
|
+
# Check input data
|
|
49
|
+
check_image(image, ignore_dtype=ignore_dtype)
|
|
50
|
+
assert isinstance(blur_strength, float | int), f"blur_strength must be a number, got {type(blur_strength)}"
|
|
51
|
+
|
|
52
|
+
# Apply Gaussian blur
|
|
53
|
+
kernel_size: int = max(3, int(blur_strength * 2) + 1)
|
|
54
|
+
if kernel_size % 2 == 0:
|
|
55
|
+
kernel_size += 1
|
|
56
|
+
blurred_image: NDArray[Any] = cv2.GaussianBlur(image, (kernel_size, kernel_size), 0)
|
|
57
|
+
|
|
58
|
+
return blurred_image
|
|
59
|
+
|
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
|
|
2
|
-
# pyright: reportUnusedImport=false
|
|
3
|
-
# ruff: noqa: F401
|
|
4
|
-
|
|
5
|
-
# Imports
|
|
6
|
-
from .common import Any, NDArray, check_image, cv2, np
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
# Functions
|
|
10
|
-
def brightness_image(image: NDArray[Any], brightness_factor: float, ignore_dtype: bool = False) -> NDArray[Any]:
|
|
11
|
-
""" Adjust the brightness of an image.
|
|
12
|
-
|
|
13
|
-
Args:
|
|
14
|
-
image (NDArray[Any]): Image to adjust brightness
|
|
15
|
-
brightness_factor (float): Brightness adjustment factor
|
|
16
|
-
ignore_dtype (bool): Ignore the dtype check
|
|
17
|
-
Returns:
|
|
18
|
-
NDArray[Any]: Image with adjusted brightness
|
|
19
|
-
|
|
20
|
-
>>> ## Basic tests
|
|
21
|
-
>>> image = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
|
|
22
|
-
>>> brightened = brightness_image(image.astype(np.uint8), 1.5)
|
|
23
|
-
>>> brightened.shape == image.shape
|
|
24
|
-
True
|
|
25
|
-
|
|
26
|
-
>>> img = np.full((3,3), 100, dtype=np.uint8)
|
|
27
|
-
>>> bright = brightness_image(img, 2.0)
|
|
28
|
-
>>> dark = brightness_image(img, 0.5)
|
|
29
|
-
>>> bool(np.mean(bright) > np.mean(img) > np.mean(dark))
|
|
30
|
-
True
|
|
31
|
-
|
|
32
|
-
>>> rgb = np.full((3,3,3), 128, dtype=np.uint8)
|
|
33
|
-
>>> bright_rgb = brightness_image(rgb, 1.5)
|
|
34
|
-
>>> bright_rgb.shape == (3,3,3)
|
|
35
|
-
True
|
|
36
|
-
|
|
37
|
-
>>> ## Test invalid inputs
|
|
38
|
-
>>> brightness_image("not an image", 1.5)
|
|
39
|
-
Traceback (most recent call last):
|
|
40
|
-
...
|
|
41
|
-
AssertionError: Image must be a numpy array
|
|
42
|
-
|
|
43
|
-
>>> brightness_image(image.astype(np.uint8), "1.5")
|
|
44
|
-
Traceback (most recent call last):
|
|
45
|
-
...
|
|
46
|
-
AssertionError: brightness_factor must be a number, got <class 'str'>
|
|
47
|
-
"""
|
|
48
|
-
# Check input data
|
|
49
|
-
check_image(image, ignore_dtype=ignore_dtype)
|
|
50
|
-
assert isinstance(brightness_factor, float | int), f"brightness_factor must be a number, got {type(brightness_factor)}"
|
|
51
|
-
|
|
52
|
-
# Apply brightness adjustment
|
|
53
|
-
return cv2.convertScaleAbs(image, alpha=brightness_factor, beta=0)
|
|
54
|
-
|
|
1
|
+
|
|
2
|
+
# pyright: reportUnusedImport=false
|
|
3
|
+
# ruff: noqa: F401
|
|
4
|
+
|
|
5
|
+
# Imports
|
|
6
|
+
from .common import Any, NDArray, check_image, cv2, np
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# Functions
|
|
10
|
+
def brightness_image(image: NDArray[Any], brightness_factor: float, ignore_dtype: bool = False) -> NDArray[Any]:
|
|
11
|
+
""" Adjust the brightness of an image.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
image (NDArray[Any]): Image to adjust brightness
|
|
15
|
+
brightness_factor (float): Brightness adjustment factor
|
|
16
|
+
ignore_dtype (bool): Ignore the dtype check
|
|
17
|
+
Returns:
|
|
18
|
+
NDArray[Any]: Image with adjusted brightness
|
|
19
|
+
|
|
20
|
+
>>> ## Basic tests
|
|
21
|
+
>>> image = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
|
|
22
|
+
>>> brightened = brightness_image(image.astype(np.uint8), 1.5)
|
|
23
|
+
>>> brightened.shape == image.shape
|
|
24
|
+
True
|
|
25
|
+
|
|
26
|
+
>>> img = np.full((3,3), 100, dtype=np.uint8)
|
|
27
|
+
>>> bright = brightness_image(img, 2.0)
|
|
28
|
+
>>> dark = brightness_image(img, 0.5)
|
|
29
|
+
>>> bool(np.mean(bright) > np.mean(img) > np.mean(dark))
|
|
30
|
+
True
|
|
31
|
+
|
|
32
|
+
>>> rgb = np.full((3,3,3), 128, dtype=np.uint8)
|
|
33
|
+
>>> bright_rgb = brightness_image(rgb, 1.5)
|
|
34
|
+
>>> bright_rgb.shape == (3,3,3)
|
|
35
|
+
True
|
|
36
|
+
|
|
37
|
+
>>> ## Test invalid inputs
|
|
38
|
+
>>> brightness_image("not an image", 1.5)
|
|
39
|
+
Traceback (most recent call last):
|
|
40
|
+
...
|
|
41
|
+
AssertionError: Image must be a numpy array
|
|
42
|
+
|
|
43
|
+
>>> brightness_image(image.astype(np.uint8), "1.5")
|
|
44
|
+
Traceback (most recent call last):
|
|
45
|
+
...
|
|
46
|
+
AssertionError: brightness_factor must be a number, got <class 'str'>
|
|
47
|
+
"""
|
|
48
|
+
# Check input data
|
|
49
|
+
check_image(image, ignore_dtype=ignore_dtype)
|
|
50
|
+
assert isinstance(brightness_factor, float | int), f"brightness_factor must be a number, got {type(brightness_factor)}"
|
|
51
|
+
|
|
52
|
+
# Apply brightness adjustment
|
|
53
|
+
return cv2.convertScaleAbs(image, alpha=brightness_factor, beta=0)
|
|
54
|
+
|