stouputils 1.14.0__py3-none-any.whl → 1.14.2__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/__init__.pyi +15 -0
- stouputils/_deprecated.pyi +12 -0
- stouputils/all_doctests.pyi +46 -0
- stouputils/applications/__init__.pyi +2 -0
- stouputils/applications/automatic_docs.py +3 -0
- stouputils/applications/automatic_docs.pyi +106 -0
- stouputils/applications/upscaler/__init__.pyi +3 -0
- stouputils/applications/upscaler/config.pyi +18 -0
- stouputils/applications/upscaler/image.pyi +109 -0
- stouputils/applications/upscaler/video.pyi +60 -0
- stouputils/archive.pyi +67 -0
- stouputils/backup.pyi +109 -0
- stouputils/collections.pyi +86 -0
- stouputils/continuous_delivery/__init__.pyi +5 -0
- stouputils/continuous_delivery/cd_utils.pyi +129 -0
- stouputils/continuous_delivery/github.pyi +162 -0
- stouputils/continuous_delivery/pypi.pyi +52 -0
- stouputils/continuous_delivery/pyproject.pyi +67 -0
- stouputils/continuous_delivery/stubs.pyi +39 -0
- stouputils/ctx.pyi +211 -0
- 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.pyi +242 -0
- stouputils/image.pyi +172 -0
- stouputils/installer/__init__.py +18 -18
- stouputils/installer/__init__.pyi +5 -0
- stouputils/installer/common.pyi +39 -0
- stouputils/installer/downloader.pyi +24 -0
- stouputils/installer/linux.py +144 -144
- stouputils/installer/linux.pyi +39 -0
- stouputils/installer/main.py +223 -223
- stouputils/installer/main.pyi +57 -0
- stouputils/installer/windows.py +136 -136
- stouputils/installer/windows.pyi +31 -0
- stouputils/io.pyi +213 -0
- stouputils/parallel.py +12 -10
- stouputils/parallel.pyi +211 -0
- stouputils/print.pyi +136 -0
- stouputils/py.typed +1 -1
- stouputils/stouputils/parallel.pyi +4 -4
- stouputils/version_pkg.pyi +15 -0
- {stouputils-1.14.0.dist-info → stouputils-1.14.2.dist-info}/METADATA +1 -1
- stouputils-1.14.2.dist-info/RECORD +171 -0
- stouputils-1.14.0.dist-info/RECORD +0 -140
- {stouputils-1.14.0.dist-info → stouputils-1.14.2.dist-info}/WHEEL +0 -0
- {stouputils-1.14.0.dist-info → stouputils-1.14.2.dist-info}/entry_points.txt +0 -0
|
@@ -1,80 +1,80 @@
|
|
|
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 rotate_image(image: NDArray[Any], angle: float | int, ignore_dtype: bool = False) -> NDArray[Any]:
|
|
11
|
-
""" Rotate an image by a given angle.
|
|
12
|
-
|
|
13
|
-
Args:
|
|
14
|
-
image (NDArray[Any]): Image to rotate
|
|
15
|
-
angle (float|int): Angle in degrees to rotate the image (between -360 and 360)
|
|
16
|
-
ignore_dtype (bool): Ignore the dtype check
|
|
17
|
-
Returns:
|
|
18
|
-
NDArray[Any]: Rotated image
|
|
19
|
-
|
|
20
|
-
>>> ## Basic tests
|
|
21
|
-
>>> image = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
|
|
22
|
-
>>> rotate_image(image.astype(np.int16), 90).tolist()
|
|
23
|
-
[[3, 6, 9], [2, 5, 8], [1, 4, 7]]
|
|
24
|
-
|
|
25
|
-
>>> rotate_image(image.astype(np.float32), 90).tolist()
|
|
26
|
-
[[3.0, 6.0, 9.0], [2.0, 5.0, 8.0], [1.0, 4.0, 7.0]]
|
|
27
|
-
|
|
28
|
-
>>> rotate_image(image.astype(np.uint8), 45).tolist()
|
|
29
|
-
[[1, 4, 4], [2, 5, 8], [2, 6, 5]]
|
|
30
|
-
|
|
31
|
-
>>> rotate_image(image.astype(np.float32), 45).tolist()
|
|
32
|
-
[[1.1875, 3.5625, 3.5625], [2.125, 5.0, 7.875], [2.375, 6.4375, 4.75]]
|
|
33
|
-
|
|
34
|
-
>>> ## Test invalid inputs
|
|
35
|
-
>>> rotate_image([1,2,3], 90)
|
|
36
|
-
Traceback (most recent call last):
|
|
37
|
-
...
|
|
38
|
-
AssertionError: Image must be a numpy array
|
|
39
|
-
|
|
40
|
-
>>> rotate_image(np.array([1,2,3]), 90)
|
|
41
|
-
Traceback (most recent call last):
|
|
42
|
-
...
|
|
43
|
-
AssertionError: Image must have at least 2 dimensions
|
|
44
|
-
|
|
45
|
-
>>> rotate_image(image.astype(np.uint8), "90")
|
|
46
|
-
Traceback (most recent call last):
|
|
47
|
-
...
|
|
48
|
-
AssertionError: Angle must be a number, got <class 'str'>
|
|
49
|
-
|
|
50
|
-
>>> rotate_image(image.astype(np.uint8), 400)
|
|
51
|
-
Traceback (most recent call last):
|
|
52
|
-
...
|
|
53
|
-
AssertionError: Angle must be between -360 and 360 degrees, got 400
|
|
54
|
-
|
|
55
|
-
>>> rotate_image(image.astype(np.int32), 90)
|
|
56
|
-
Traceback (most recent call last):
|
|
57
|
-
...
|
|
58
|
-
AssertionError: Image must be of type [<class 'numpy.uint8'>, <class 'numpy.int16'>, <class 'numpy.float32'>, <class 'numpy.float64'>]
|
|
59
|
-
""" # noqa: E501
|
|
60
|
-
# Check input data
|
|
61
|
-
check_image(image, ignore_dtype=ignore_dtype)
|
|
62
|
-
assert isinstance(angle, float | int), f"Angle must be a number, got {type(angle)}"
|
|
63
|
-
assert -360 <= angle <= 360, f"Angle must be between -360 and 360 degrees, got {angle}"
|
|
64
|
-
|
|
65
|
-
# Get image dimensions
|
|
66
|
-
height: int
|
|
67
|
-
width: int
|
|
68
|
-
height, width = image.shape[:2]
|
|
69
|
-
image_center: tuple[int, int] = (width // 2, height // 2)
|
|
70
|
-
|
|
71
|
-
# Get rotation matrix and rotate image
|
|
72
|
-
rotation_matrix: NDArray[Any] = cv2.getRotationMatrix2D(image_center, angle, 1.0)
|
|
73
|
-
rotated_image: NDArray[Any] = cv2.warpAffine(
|
|
74
|
-
image,
|
|
75
|
-
rotation_matrix,
|
|
76
|
-
(width, height)
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
return rotated_image
|
|
80
|
-
|
|
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 rotate_image(image: NDArray[Any], angle: float | int, ignore_dtype: bool = False) -> NDArray[Any]:
|
|
11
|
+
""" Rotate an image by a given angle.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
image (NDArray[Any]): Image to rotate
|
|
15
|
+
angle (float|int): Angle in degrees to rotate the image (between -360 and 360)
|
|
16
|
+
ignore_dtype (bool): Ignore the dtype check
|
|
17
|
+
Returns:
|
|
18
|
+
NDArray[Any]: Rotated image
|
|
19
|
+
|
|
20
|
+
>>> ## Basic tests
|
|
21
|
+
>>> image = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
|
|
22
|
+
>>> rotate_image(image.astype(np.int16), 90).tolist()
|
|
23
|
+
[[3, 6, 9], [2, 5, 8], [1, 4, 7]]
|
|
24
|
+
|
|
25
|
+
>>> rotate_image(image.astype(np.float32), 90).tolist()
|
|
26
|
+
[[3.0, 6.0, 9.0], [2.0, 5.0, 8.0], [1.0, 4.0, 7.0]]
|
|
27
|
+
|
|
28
|
+
>>> rotate_image(image.astype(np.uint8), 45).tolist()
|
|
29
|
+
[[1, 4, 4], [2, 5, 8], [2, 6, 5]]
|
|
30
|
+
|
|
31
|
+
>>> rotate_image(image.astype(np.float32), 45).tolist()
|
|
32
|
+
[[1.1875, 3.5625, 3.5625], [2.125, 5.0, 7.875], [2.375, 6.4375, 4.75]]
|
|
33
|
+
|
|
34
|
+
>>> ## Test invalid inputs
|
|
35
|
+
>>> rotate_image([1,2,3], 90)
|
|
36
|
+
Traceback (most recent call last):
|
|
37
|
+
...
|
|
38
|
+
AssertionError: Image must be a numpy array
|
|
39
|
+
|
|
40
|
+
>>> rotate_image(np.array([1,2,3]), 90)
|
|
41
|
+
Traceback (most recent call last):
|
|
42
|
+
...
|
|
43
|
+
AssertionError: Image must have at least 2 dimensions
|
|
44
|
+
|
|
45
|
+
>>> rotate_image(image.astype(np.uint8), "90")
|
|
46
|
+
Traceback (most recent call last):
|
|
47
|
+
...
|
|
48
|
+
AssertionError: Angle must be a number, got <class 'str'>
|
|
49
|
+
|
|
50
|
+
>>> rotate_image(image.astype(np.uint8), 400)
|
|
51
|
+
Traceback (most recent call last):
|
|
52
|
+
...
|
|
53
|
+
AssertionError: Angle must be between -360 and 360 degrees, got 400
|
|
54
|
+
|
|
55
|
+
>>> rotate_image(image.astype(np.int32), 90)
|
|
56
|
+
Traceback (most recent call last):
|
|
57
|
+
...
|
|
58
|
+
AssertionError: Image must be of type [<class 'numpy.uint8'>, <class 'numpy.int16'>, <class 'numpy.float32'>, <class 'numpy.float64'>]
|
|
59
|
+
""" # noqa: E501
|
|
60
|
+
# Check input data
|
|
61
|
+
check_image(image, ignore_dtype=ignore_dtype)
|
|
62
|
+
assert isinstance(angle, float | int), f"Angle must be a number, got {type(angle)}"
|
|
63
|
+
assert -360 <= angle <= 360, f"Angle must be between -360 and 360 degrees, got {angle}"
|
|
64
|
+
|
|
65
|
+
# Get image dimensions
|
|
66
|
+
height: int
|
|
67
|
+
width: int
|
|
68
|
+
height, width = image.shape[:2]
|
|
69
|
+
image_center: tuple[int, int] = (width // 2, height // 2)
|
|
70
|
+
|
|
71
|
+
# Get rotation matrix and rotate image
|
|
72
|
+
rotation_matrix: NDArray[Any] = cv2.getRotationMatrix2D(image_center, angle, 1.0)
|
|
73
|
+
rotated_image: NDArray[Any] = cv2.warpAffine(
|
|
74
|
+
image,
|
|
75
|
+
rotation_matrix,
|
|
76
|
+
(width, height)
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
return rotated_image
|
|
80
|
+
|
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
|
|
2
|
-
# Imports
|
|
3
|
-
from .common import Any, NDArray, check_image, np
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
# Functions
|
|
7
|
-
def salt_pepper_image(image: NDArray[Any], density: float, ignore_dtype: bool = False) -> NDArray[Any]:
|
|
8
|
-
""" Add salt and pepper noise to an image.
|
|
9
|
-
|
|
10
|
-
Args:
|
|
11
|
-
image (NDArray[Any]): Image to add noise to
|
|
12
|
-
density (float): Density of the noise (between 0 and 1)
|
|
13
|
-
ignore_dtype (bool): Ignore the dtype check
|
|
14
|
-
Returns:
|
|
15
|
-
NDArray[Any]: Image with salt and pepper noise
|
|
16
|
-
|
|
17
|
-
>>> ## Basic tests
|
|
18
|
-
>>> image = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
|
|
19
|
-
>>> noisy = salt_pepper_image(image.astype(np.uint8), 0.1)
|
|
20
|
-
>>> noisy.shape == image.shape
|
|
21
|
-
True
|
|
22
|
-
|
|
23
|
-
>>> np.random.seed(42)
|
|
24
|
-
>>> img = np.full((4,4), 128, dtype=np.uint8)
|
|
25
|
-
>>> noisy = salt_pepper_image(img, 1.0)
|
|
26
|
-
>>> sorted(np.unique(noisy).tolist()) # Should only contain 0 and 255
|
|
27
|
-
[0, 255]
|
|
28
|
-
|
|
29
|
-
>>> rgb = np.full((3,3,3), 128, dtype=np.uint8)
|
|
30
|
-
>>> noisy_rgb = salt_pepper_image(rgb, 0.1)
|
|
31
|
-
>>> noisy_rgb.shape == (3,3,3)
|
|
32
|
-
True
|
|
33
|
-
|
|
34
|
-
>>> ## Test invalid inputs
|
|
35
|
-
>>> salt_pepper_image("not an image", 0.1)
|
|
36
|
-
Traceback (most recent call last):
|
|
37
|
-
...
|
|
38
|
-
AssertionError: Image must be a numpy array
|
|
39
|
-
|
|
40
|
-
>>> salt_pepper_image(image.astype(np.uint8), "0.1")
|
|
41
|
-
Traceback (most recent call last):
|
|
42
|
-
...
|
|
43
|
-
AssertionError: density must be a number, got <class 'str'>
|
|
44
|
-
|
|
45
|
-
>>> salt_pepper_image(image.astype(np.uint8), 1.5)
|
|
46
|
-
Traceback (most recent call last):
|
|
47
|
-
...
|
|
48
|
-
AssertionError: density must be between 0 and 1, got 1.5
|
|
49
|
-
"""
|
|
50
|
-
# Check input data
|
|
51
|
-
check_image(image, ignore_dtype=ignore_dtype)
|
|
52
|
-
assert isinstance(density, float | int), f"density must be a number, got {type(density)}"
|
|
53
|
-
assert 0 <= density <= 1, f"density must be between 0 and 1, got {density}"
|
|
54
|
-
|
|
55
|
-
# Create a mask of the same shape as the input image
|
|
56
|
-
mask: NDArray[Any] = np.random.choice( # pyright: ignore [reportUnknownMemberType]
|
|
57
|
-
[0, 1, 2],
|
|
58
|
-
size=image.shape,
|
|
59
|
-
p=[1-density, density/2, density/2],
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
# Apply the mask to the input image
|
|
63
|
-
noisy_image: NDArray[Any] = image.copy()
|
|
64
|
-
noisy_image[mask == 1] = 0 # Pepper noise
|
|
65
|
-
noisy_image[mask == 2] = 255 # Salt noise
|
|
66
|
-
|
|
67
|
-
return noisy_image
|
|
68
|
-
|
|
1
|
+
|
|
2
|
+
# Imports
|
|
3
|
+
from .common import Any, NDArray, check_image, np
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# Functions
|
|
7
|
+
def salt_pepper_image(image: NDArray[Any], density: float, ignore_dtype: bool = False) -> NDArray[Any]:
|
|
8
|
+
""" Add salt and pepper noise to an image.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
image (NDArray[Any]): Image to add noise to
|
|
12
|
+
density (float): Density of the noise (between 0 and 1)
|
|
13
|
+
ignore_dtype (bool): Ignore the dtype check
|
|
14
|
+
Returns:
|
|
15
|
+
NDArray[Any]: Image with salt and pepper noise
|
|
16
|
+
|
|
17
|
+
>>> ## Basic tests
|
|
18
|
+
>>> image = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
|
|
19
|
+
>>> noisy = salt_pepper_image(image.astype(np.uint8), 0.1)
|
|
20
|
+
>>> noisy.shape == image.shape
|
|
21
|
+
True
|
|
22
|
+
|
|
23
|
+
>>> np.random.seed(42)
|
|
24
|
+
>>> img = np.full((4,4), 128, dtype=np.uint8)
|
|
25
|
+
>>> noisy = salt_pepper_image(img, 1.0)
|
|
26
|
+
>>> sorted(np.unique(noisy).tolist()) # Should only contain 0 and 255
|
|
27
|
+
[0, 255]
|
|
28
|
+
|
|
29
|
+
>>> rgb = np.full((3,3,3), 128, dtype=np.uint8)
|
|
30
|
+
>>> noisy_rgb = salt_pepper_image(rgb, 0.1)
|
|
31
|
+
>>> noisy_rgb.shape == (3,3,3)
|
|
32
|
+
True
|
|
33
|
+
|
|
34
|
+
>>> ## Test invalid inputs
|
|
35
|
+
>>> salt_pepper_image("not an image", 0.1)
|
|
36
|
+
Traceback (most recent call last):
|
|
37
|
+
...
|
|
38
|
+
AssertionError: Image must be a numpy array
|
|
39
|
+
|
|
40
|
+
>>> salt_pepper_image(image.astype(np.uint8), "0.1")
|
|
41
|
+
Traceback (most recent call last):
|
|
42
|
+
...
|
|
43
|
+
AssertionError: density must be a number, got <class 'str'>
|
|
44
|
+
|
|
45
|
+
>>> salt_pepper_image(image.astype(np.uint8), 1.5)
|
|
46
|
+
Traceback (most recent call last):
|
|
47
|
+
...
|
|
48
|
+
AssertionError: density must be between 0 and 1, got 1.5
|
|
49
|
+
"""
|
|
50
|
+
# Check input data
|
|
51
|
+
check_image(image, ignore_dtype=ignore_dtype)
|
|
52
|
+
assert isinstance(density, float | int), f"density must be a number, got {type(density)}"
|
|
53
|
+
assert 0 <= density <= 1, f"density must be between 0 and 1, got {density}"
|
|
54
|
+
|
|
55
|
+
# Create a mask of the same shape as the input image
|
|
56
|
+
mask: NDArray[Any] = np.random.choice( # pyright: ignore [reportUnknownMemberType]
|
|
57
|
+
[0, 1, 2],
|
|
58
|
+
size=image.shape,
|
|
59
|
+
p=[1-density, density/2, density/2],
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Apply the mask to the input image
|
|
63
|
+
noisy_image: NDArray[Any] = image.copy()
|
|
64
|
+
noisy_image[mask == 1] = 0 # Pepper noise
|
|
65
|
+
noisy_image[mask == 2] = 255 # Salt noise
|
|
66
|
+
|
|
67
|
+
return noisy_image
|
|
68
|
+
|
|
@@ -1,55 +1,55 @@
|
|
|
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 sharpen_image(image: NDArray[Any], alpha: float, ignore_dtype: bool = False) -> NDArray[Any]:
|
|
11
|
-
""" Sharpen an image.
|
|
12
|
-
|
|
13
|
-
Args:
|
|
14
|
-
image (NDArray[Any]): Image to sharpen
|
|
15
|
-
alpha (float): Sharpening factor
|
|
16
|
-
ignore_dtype (bool): Ignore the dtype check
|
|
17
|
-
Returns:
|
|
18
|
-
NDArray[Any]: Sharpened image
|
|
19
|
-
|
|
20
|
-
>>> ## Basic tests
|
|
21
|
-
>>> image = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
|
|
22
|
-
>>> sharpened = sharpen_image(image.astype(np.uint8), 1.5)
|
|
23
|
-
>>> sharpened.shape == image.shape
|
|
24
|
-
True
|
|
25
|
-
|
|
26
|
-
>>> img = np.full((5,5), 128, dtype=np.uint8)
|
|
27
|
-
>>> img[2,2] = 255 # Center bright pixel
|
|
28
|
-
>>> sharp = sharpen_image(img, 1.0)
|
|
29
|
-
>>> bool(sharp[2,2] > img[2,2] * 0.9) # Center should stay bright
|
|
30
|
-
True
|
|
31
|
-
|
|
32
|
-
>>> rgb = np.full((3,3,3), 128, dtype=np.uint8)
|
|
33
|
-
>>> sharp_rgb = sharpen_image(rgb, 1.0)
|
|
34
|
-
>>> sharp_rgb.shape == (3,3,3)
|
|
35
|
-
True
|
|
36
|
-
|
|
37
|
-
>>> ## Test invalid inputs
|
|
38
|
-
>>> sharpen_image("not an image", 1.5)
|
|
39
|
-
Traceback (most recent call last):
|
|
40
|
-
...
|
|
41
|
-
AssertionError: Image must be a numpy array
|
|
42
|
-
|
|
43
|
-
>>> sharpen_image(image.astype(np.uint8), "1.5")
|
|
44
|
-
Traceback (most recent call last):
|
|
45
|
-
...
|
|
46
|
-
AssertionError: alpha must be a number, got <class 'str'>
|
|
47
|
-
"""
|
|
48
|
-
# Check input data
|
|
49
|
-
check_image(image, ignore_dtype=ignore_dtype)
|
|
50
|
-
assert isinstance(alpha, float | int), f"alpha must be a number, got {type(alpha)}"
|
|
51
|
-
|
|
52
|
-
# Apply sharpening
|
|
53
|
-
blurred: NDArray[Any] = cv2.GaussianBlur(image, (0, 0), 3)
|
|
54
|
-
return cv2.addWeighted(image, 1 + alpha, blurred, -alpha, 0)
|
|
55
|
-
|
|
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 sharpen_image(image: NDArray[Any], alpha: float, ignore_dtype: bool = False) -> NDArray[Any]:
|
|
11
|
+
""" Sharpen an image.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
image (NDArray[Any]): Image to sharpen
|
|
15
|
+
alpha (float): Sharpening factor
|
|
16
|
+
ignore_dtype (bool): Ignore the dtype check
|
|
17
|
+
Returns:
|
|
18
|
+
NDArray[Any]: Sharpened image
|
|
19
|
+
|
|
20
|
+
>>> ## Basic tests
|
|
21
|
+
>>> image = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
|
|
22
|
+
>>> sharpened = sharpen_image(image.astype(np.uint8), 1.5)
|
|
23
|
+
>>> sharpened.shape == image.shape
|
|
24
|
+
True
|
|
25
|
+
|
|
26
|
+
>>> img = np.full((5,5), 128, dtype=np.uint8)
|
|
27
|
+
>>> img[2,2] = 255 # Center bright pixel
|
|
28
|
+
>>> sharp = sharpen_image(img, 1.0)
|
|
29
|
+
>>> bool(sharp[2,2] > img[2,2] * 0.9) # Center should stay bright
|
|
30
|
+
True
|
|
31
|
+
|
|
32
|
+
>>> rgb = np.full((3,3,3), 128, dtype=np.uint8)
|
|
33
|
+
>>> sharp_rgb = sharpen_image(rgb, 1.0)
|
|
34
|
+
>>> sharp_rgb.shape == (3,3,3)
|
|
35
|
+
True
|
|
36
|
+
|
|
37
|
+
>>> ## Test invalid inputs
|
|
38
|
+
>>> sharpen_image("not an image", 1.5)
|
|
39
|
+
Traceback (most recent call last):
|
|
40
|
+
...
|
|
41
|
+
AssertionError: Image must be a numpy array
|
|
42
|
+
|
|
43
|
+
>>> sharpen_image(image.astype(np.uint8), "1.5")
|
|
44
|
+
Traceback (most recent call last):
|
|
45
|
+
...
|
|
46
|
+
AssertionError: alpha must be a number, got <class 'str'>
|
|
47
|
+
"""
|
|
48
|
+
# Check input data
|
|
49
|
+
check_image(image, ignore_dtype=ignore_dtype)
|
|
50
|
+
assert isinstance(alpha, float | int), f"alpha must be a number, got {type(alpha)}"
|
|
51
|
+
|
|
52
|
+
# Apply sharpening
|
|
53
|
+
blurred: NDArray[Any] = cv2.GaussianBlur(image, (0, 0), 3)
|
|
54
|
+
return cv2.addWeighted(image, 1 + alpha, blurred, -alpha, 0)
|
|
55
|
+
|
|
@@ -1,64 +1,64 @@
|
|
|
1
|
-
|
|
2
|
-
# Imports
|
|
3
|
-
from .common import Any, NDArray, check_image, cv2, np
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
# Functions
|
|
7
|
-
def shear_image(image: NDArray[Any], x: float, y: float, ignore_dtype: bool = False) -> NDArray[Any]:
|
|
8
|
-
""" Shear an image
|
|
9
|
-
|
|
10
|
-
Args:
|
|
11
|
-
image (NDArray[Any]): Image to shear
|
|
12
|
-
x (float): Shearing along the x axis (between -180 and 180)
|
|
13
|
-
y (float): Shearing along the y axis (between -180 and 180)
|
|
14
|
-
ignore_dtype (bool): Ignore the dtype check
|
|
15
|
-
Returns:
|
|
16
|
-
NDArray[Any]: Sheared image
|
|
17
|
-
|
|
18
|
-
>>> ## Basic tests
|
|
19
|
-
>>> image = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
|
|
20
|
-
>>> shear_image(image.astype(np.uint8), 15, 0).tolist()
|
|
21
|
-
[[1, 2, 3], [3, 5, 6], [3, 7, 8]]
|
|
22
|
-
|
|
23
|
-
>>> shear_image(image.astype(np.float32), 0, 15).tolist()
|
|
24
|
-
[[1.0, 1.4375, 1.40625], [4.0, 4.15625, 4.40625], [7.0, 7.15625, 7.40625]]
|
|
25
|
-
|
|
26
|
-
>>> ## Test invalid inputs
|
|
27
|
-
>>> shear_image(image.astype(np.uint8), 200, 0)
|
|
28
|
-
Traceback (most recent call last):
|
|
29
|
-
...
|
|
30
|
-
AssertionError: x must be between -180 and 180, got 200
|
|
31
|
-
|
|
32
|
-
>>> shear_image(image.astype(np.uint8), 0, -200)
|
|
33
|
-
Traceback (most recent call last):
|
|
34
|
-
...
|
|
35
|
-
AssertionError: y must be between -180 and 180, got -200
|
|
36
|
-
|
|
37
|
-
>>> shear_image("not an image", 0, 0)
|
|
38
|
-
Traceback (most recent call last):
|
|
39
|
-
...
|
|
40
|
-
AssertionError: Image must be a numpy array
|
|
41
|
-
"""
|
|
42
|
-
# Check input data
|
|
43
|
-
check_image(image, ignore_dtype=ignore_dtype)
|
|
44
|
-
assert isinstance(x, float | int), f"x must be a number, got {type(x)}"
|
|
45
|
-
assert isinstance(y, float | int), f"y must be a number, got {type(y)}"
|
|
46
|
-
assert -180 <= x <= 180, f"x must be between -180 and 180, got {x}"
|
|
47
|
-
assert -180 <= y <= 180, f"y must be between -180 and 180, got {y}"
|
|
48
|
-
|
|
49
|
-
# Get image dimensions
|
|
50
|
-
height, width = image.shape[:2]
|
|
51
|
-
|
|
52
|
-
# Convert relative shear angles to absolute values
|
|
53
|
-
x_shear: float = np.tan(np.radians(x))
|
|
54
|
-
y_shear: float = np.tan(np.radians(y))
|
|
55
|
-
|
|
56
|
-
# Create shear matrix
|
|
57
|
-
shear_matrix: NDArray[Any] = np.array([
|
|
58
|
-
[1, x_shear, 0],
|
|
59
|
-
[y_shear, 1, 0]
|
|
60
|
-
], dtype=np.float32)
|
|
61
|
-
|
|
62
|
-
# Apply affine transformation
|
|
63
|
-
return cv2.warpAffine(image, shear_matrix, (width, height))
|
|
64
|
-
|
|
1
|
+
|
|
2
|
+
# Imports
|
|
3
|
+
from .common import Any, NDArray, check_image, cv2, np
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# Functions
|
|
7
|
+
def shear_image(image: NDArray[Any], x: float, y: float, ignore_dtype: bool = False) -> NDArray[Any]:
|
|
8
|
+
""" Shear an image
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
image (NDArray[Any]): Image to shear
|
|
12
|
+
x (float): Shearing along the x axis (between -180 and 180)
|
|
13
|
+
y (float): Shearing along the y axis (between -180 and 180)
|
|
14
|
+
ignore_dtype (bool): Ignore the dtype check
|
|
15
|
+
Returns:
|
|
16
|
+
NDArray[Any]: Sheared image
|
|
17
|
+
|
|
18
|
+
>>> ## Basic tests
|
|
19
|
+
>>> image = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
|
|
20
|
+
>>> shear_image(image.astype(np.uint8), 15, 0).tolist()
|
|
21
|
+
[[1, 2, 3], [3, 5, 6], [3, 7, 8]]
|
|
22
|
+
|
|
23
|
+
>>> shear_image(image.astype(np.float32), 0, 15).tolist()
|
|
24
|
+
[[1.0, 1.4375, 1.40625], [4.0, 4.15625, 4.40625], [7.0, 7.15625, 7.40625]]
|
|
25
|
+
|
|
26
|
+
>>> ## Test invalid inputs
|
|
27
|
+
>>> shear_image(image.astype(np.uint8), 200, 0)
|
|
28
|
+
Traceback (most recent call last):
|
|
29
|
+
...
|
|
30
|
+
AssertionError: x must be between -180 and 180, got 200
|
|
31
|
+
|
|
32
|
+
>>> shear_image(image.astype(np.uint8), 0, -200)
|
|
33
|
+
Traceback (most recent call last):
|
|
34
|
+
...
|
|
35
|
+
AssertionError: y must be between -180 and 180, got -200
|
|
36
|
+
|
|
37
|
+
>>> shear_image("not an image", 0, 0)
|
|
38
|
+
Traceback (most recent call last):
|
|
39
|
+
...
|
|
40
|
+
AssertionError: Image must be a numpy array
|
|
41
|
+
"""
|
|
42
|
+
# Check input data
|
|
43
|
+
check_image(image, ignore_dtype=ignore_dtype)
|
|
44
|
+
assert isinstance(x, float | int), f"x must be a number, got {type(x)}"
|
|
45
|
+
assert isinstance(y, float | int), f"y must be a number, got {type(y)}"
|
|
46
|
+
assert -180 <= x <= 180, f"x must be between -180 and 180, got {x}"
|
|
47
|
+
assert -180 <= y <= 180, f"y must be between -180 and 180, got {y}"
|
|
48
|
+
|
|
49
|
+
# Get image dimensions
|
|
50
|
+
height, width = image.shape[:2]
|
|
51
|
+
|
|
52
|
+
# Convert relative shear angles to absolute values
|
|
53
|
+
x_shear: float = np.tan(np.radians(x))
|
|
54
|
+
y_shear: float = np.tan(np.radians(y))
|
|
55
|
+
|
|
56
|
+
# Create shear matrix
|
|
57
|
+
shear_matrix: NDArray[Any] = np.array([
|
|
58
|
+
[1, x_shear, 0],
|
|
59
|
+
[y_shear, 1, 0]
|
|
60
|
+
], dtype=np.float32)
|
|
61
|
+
|
|
62
|
+
# Apply affine transformation
|
|
63
|
+
return cv2.warpAffine(image, shear_matrix, (width, height))
|
|
64
|
+
|
|
@@ -1,64 +1,64 @@
|
|
|
1
|
-
|
|
2
|
-
# Imports
|
|
3
|
-
from .common import Any, NDArray, check_image, cv2, np
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
# Functions
|
|
7
|
-
def threshold_image(image: NDArray[Any], thresholds: list[float], ignore_dtype: bool = False) -> NDArray[Any]:
|
|
8
|
-
""" Apply multi-level threshold to an image.
|
|
9
|
-
|
|
10
|
-
Args:
|
|
11
|
-
image (NDArray[Any]): Image to threshold
|
|
12
|
-
threshold (list[float]): List of threshold values (between 0 and 1)
|
|
13
|
-
ignore_dtype (bool): Ignore the dtype check
|
|
14
|
-
Returns:
|
|
15
|
-
NDArray[Any]: Multi-level thresholded image
|
|
16
|
-
|
|
17
|
-
>>> ## Basic tests
|
|
18
|
-
>>> image = np.array([[100, 150, 200], [50, 125, 175], [25, 75, 225]])
|
|
19
|
-
>>> threshold_image(image.astype(np.uint8), [0.3, 0.6]).tolist()
|
|
20
|
-
[[85, 85, 170], [0, 85, 170], [0, 0, 170]]
|
|
21
|
-
|
|
22
|
-
>>> rgb = np.random.randint(0, 256, (3,3,3), dtype=np.uint8)
|
|
23
|
-
>>> thresh_rgb = threshold_image(rgb, [0.3, 0.6])
|
|
24
|
-
>>> thresh_rgb.shape == rgb.shape
|
|
25
|
-
True
|
|
26
|
-
|
|
27
|
-
>>> ## Test invalid inputs
|
|
28
|
-
>>> threshold_image("not an image", [0.5])
|
|
29
|
-
Traceback (most recent call last):
|
|
30
|
-
...
|
|
31
|
-
AssertionError: Image must be a numpy array
|
|
32
|
-
|
|
33
|
-
>>> threshold_image(image.astype(np.uint8), [1.5])
|
|
34
|
-
Traceback (most recent call last):
|
|
35
|
-
...
|
|
36
|
-
AssertionError: threshold values must be between 0 and 1, got [1.5]
|
|
37
|
-
"""
|
|
38
|
-
# Check input data
|
|
39
|
-
check_image(image, ignore_dtype=ignore_dtype)
|
|
40
|
-
assert all(0 <= t <= 1 for t in thresholds), f"threshold values must be between 0 and 1, got {thresholds}"
|
|
41
|
-
|
|
42
|
-
# Convert thresholds from 0-1 range to 0-255 range
|
|
43
|
-
threshold_values: list[int] = [int(t * 255) for t in sorted(thresholds)]
|
|
44
|
-
|
|
45
|
-
# Apply threshold
|
|
46
|
-
if len(image.shape) == 2:
|
|
47
|
-
# Grayscale image
|
|
48
|
-
result: NDArray[Any] = np.zeros_like(image)
|
|
49
|
-
for i, thresh in enumerate(threshold_values):
|
|
50
|
-
value: int = int(255 * (i + 1) / (len(threshold_values) + 1))
|
|
51
|
-
mask: NDArray[Any] = cv2.threshold(image, thresh, value, cv2.THRESH_BINARY)[1]
|
|
52
|
-
result = cv2.max(result, mask)
|
|
53
|
-
else:
|
|
54
|
-
# Color image - convert to grayscale first, then back to color
|
|
55
|
-
gray: NDArray[Any] = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
|
56
|
-
result: NDArray[Any] = np.zeros_like(gray)
|
|
57
|
-
for i, thresh in enumerate(threshold_values):
|
|
58
|
-
value: int = int(255 * (i + 1) / (len(threshold_values) + 1))
|
|
59
|
-
mask: NDArray[Any] = cv2.threshold(gray, thresh, value, cv2.THRESH_BINARY)[1]
|
|
60
|
-
result = cv2.max(result, mask)
|
|
61
|
-
result = cv2.cvtColor(result, cv2.COLOR_GRAY2BGR)
|
|
62
|
-
|
|
63
|
-
return result
|
|
64
|
-
|
|
1
|
+
|
|
2
|
+
# Imports
|
|
3
|
+
from .common import Any, NDArray, check_image, cv2, np
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# Functions
|
|
7
|
+
def threshold_image(image: NDArray[Any], thresholds: list[float], ignore_dtype: bool = False) -> NDArray[Any]:
|
|
8
|
+
""" Apply multi-level threshold to an image.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
image (NDArray[Any]): Image to threshold
|
|
12
|
+
threshold (list[float]): List of threshold values (between 0 and 1)
|
|
13
|
+
ignore_dtype (bool): Ignore the dtype check
|
|
14
|
+
Returns:
|
|
15
|
+
NDArray[Any]: Multi-level thresholded image
|
|
16
|
+
|
|
17
|
+
>>> ## Basic tests
|
|
18
|
+
>>> image = np.array([[100, 150, 200], [50, 125, 175], [25, 75, 225]])
|
|
19
|
+
>>> threshold_image(image.astype(np.uint8), [0.3, 0.6]).tolist()
|
|
20
|
+
[[85, 85, 170], [0, 85, 170], [0, 0, 170]]
|
|
21
|
+
|
|
22
|
+
>>> rgb = np.random.randint(0, 256, (3,3,3), dtype=np.uint8)
|
|
23
|
+
>>> thresh_rgb = threshold_image(rgb, [0.3, 0.6])
|
|
24
|
+
>>> thresh_rgb.shape == rgb.shape
|
|
25
|
+
True
|
|
26
|
+
|
|
27
|
+
>>> ## Test invalid inputs
|
|
28
|
+
>>> threshold_image("not an image", [0.5])
|
|
29
|
+
Traceback (most recent call last):
|
|
30
|
+
...
|
|
31
|
+
AssertionError: Image must be a numpy array
|
|
32
|
+
|
|
33
|
+
>>> threshold_image(image.astype(np.uint8), [1.5])
|
|
34
|
+
Traceback (most recent call last):
|
|
35
|
+
...
|
|
36
|
+
AssertionError: threshold values must be between 0 and 1, got [1.5]
|
|
37
|
+
"""
|
|
38
|
+
# Check input data
|
|
39
|
+
check_image(image, ignore_dtype=ignore_dtype)
|
|
40
|
+
assert all(0 <= t <= 1 for t in thresholds), f"threshold values must be between 0 and 1, got {thresholds}"
|
|
41
|
+
|
|
42
|
+
# Convert thresholds from 0-1 range to 0-255 range
|
|
43
|
+
threshold_values: list[int] = [int(t * 255) for t in sorted(thresholds)]
|
|
44
|
+
|
|
45
|
+
# Apply threshold
|
|
46
|
+
if len(image.shape) == 2:
|
|
47
|
+
# Grayscale image
|
|
48
|
+
result: NDArray[Any] = np.zeros_like(image)
|
|
49
|
+
for i, thresh in enumerate(threshold_values):
|
|
50
|
+
value: int = int(255 * (i + 1) / (len(threshold_values) + 1))
|
|
51
|
+
mask: NDArray[Any] = cv2.threshold(image, thresh, value, cv2.THRESH_BINARY)[1]
|
|
52
|
+
result = cv2.max(result, mask)
|
|
53
|
+
else:
|
|
54
|
+
# Color image - convert to grayscale first, then back to color
|
|
55
|
+
gray: NDArray[Any] = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
|
56
|
+
result: NDArray[Any] = np.zeros_like(gray)
|
|
57
|
+
for i, thresh in enumerate(threshold_values):
|
|
58
|
+
value: int = int(255 * (i + 1) / (len(threshold_values) + 1))
|
|
59
|
+
mask: NDArray[Any] = cv2.threshold(gray, thresh, value, cv2.THRESH_BINARY)[1]
|
|
60
|
+
result = cv2.max(result, mask)
|
|
61
|
+
result = cv2.cvtColor(result, cv2.COLOR_GRAY2BGR)
|
|
62
|
+
|
|
63
|
+
return result
|
|
64
|
+
|