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.
Files changed (108) hide show
  1. stouputils/__init__.pyi +15 -0
  2. stouputils/_deprecated.pyi +12 -0
  3. stouputils/all_doctests.pyi +46 -0
  4. stouputils/applications/__init__.pyi +2 -0
  5. stouputils/applications/automatic_docs.py +3 -0
  6. stouputils/applications/automatic_docs.pyi +106 -0
  7. stouputils/applications/upscaler/__init__.pyi +3 -0
  8. stouputils/applications/upscaler/config.pyi +18 -0
  9. stouputils/applications/upscaler/image.pyi +109 -0
  10. stouputils/applications/upscaler/video.pyi +60 -0
  11. stouputils/archive.pyi +67 -0
  12. stouputils/backup.pyi +109 -0
  13. stouputils/collections.pyi +86 -0
  14. stouputils/continuous_delivery/__init__.pyi +5 -0
  15. stouputils/continuous_delivery/cd_utils.pyi +129 -0
  16. stouputils/continuous_delivery/github.pyi +162 -0
  17. stouputils/continuous_delivery/pypi.pyi +52 -0
  18. stouputils/continuous_delivery/pyproject.pyi +67 -0
  19. stouputils/continuous_delivery/stubs.pyi +39 -0
  20. stouputils/ctx.pyi +211 -0
  21. stouputils/data_science/config/get.py +51 -51
  22. stouputils/data_science/data_processing/image/__init__.py +66 -66
  23. stouputils/data_science/data_processing/image/auto_contrast.py +79 -79
  24. stouputils/data_science/data_processing/image/axis_flip.py +58 -58
  25. stouputils/data_science/data_processing/image/bias_field_correction.py +74 -74
  26. stouputils/data_science/data_processing/image/binary_threshold.py +73 -73
  27. stouputils/data_science/data_processing/image/blur.py +59 -59
  28. stouputils/data_science/data_processing/image/brightness.py +54 -54
  29. stouputils/data_science/data_processing/image/canny.py +110 -110
  30. stouputils/data_science/data_processing/image/clahe.py +92 -92
  31. stouputils/data_science/data_processing/image/common.py +30 -30
  32. stouputils/data_science/data_processing/image/contrast.py +53 -53
  33. stouputils/data_science/data_processing/image/curvature_flow_filter.py +74 -74
  34. stouputils/data_science/data_processing/image/denoise.py +378 -378
  35. stouputils/data_science/data_processing/image/histogram_equalization.py +123 -123
  36. stouputils/data_science/data_processing/image/invert.py +64 -64
  37. stouputils/data_science/data_processing/image/laplacian.py +60 -60
  38. stouputils/data_science/data_processing/image/median_blur.py +52 -52
  39. stouputils/data_science/data_processing/image/noise.py +59 -59
  40. stouputils/data_science/data_processing/image/normalize.py +65 -65
  41. stouputils/data_science/data_processing/image/random_erase.py +66 -66
  42. stouputils/data_science/data_processing/image/resize.py +69 -69
  43. stouputils/data_science/data_processing/image/rotation.py +80 -80
  44. stouputils/data_science/data_processing/image/salt_pepper.py +68 -68
  45. stouputils/data_science/data_processing/image/sharpening.py +55 -55
  46. stouputils/data_science/data_processing/image/shearing.py +64 -64
  47. stouputils/data_science/data_processing/image/threshold.py +64 -64
  48. stouputils/data_science/data_processing/image/translation.py +71 -71
  49. stouputils/data_science/data_processing/image/zoom.py +83 -83
  50. stouputils/data_science/data_processing/image_augmentation.py +118 -118
  51. stouputils/data_science/data_processing/image_preprocess.py +183 -183
  52. stouputils/data_science/data_processing/prosthesis_detection.py +359 -359
  53. stouputils/data_science/data_processing/technique.py +481 -481
  54. stouputils/data_science/dataset/__init__.py +45 -45
  55. stouputils/data_science/dataset/dataset.py +292 -292
  56. stouputils/data_science/dataset/dataset_loader.py +135 -135
  57. stouputils/data_science/dataset/grouping_strategy.py +296 -296
  58. stouputils/data_science/dataset/image_loader.py +100 -100
  59. stouputils/data_science/dataset/xy_tuple.py +696 -696
  60. stouputils/data_science/metric_dictionnary.py +106 -106
  61. stouputils/data_science/mlflow_utils.py +206 -206
  62. stouputils/data_science/models/abstract_model.py +149 -149
  63. stouputils/data_science/models/all.py +85 -85
  64. stouputils/data_science/models/keras/all.py +38 -38
  65. stouputils/data_science/models/keras/convnext.py +62 -62
  66. stouputils/data_science/models/keras/densenet.py +50 -50
  67. stouputils/data_science/models/keras/efficientnet.py +60 -60
  68. stouputils/data_science/models/keras/mobilenet.py +56 -56
  69. stouputils/data_science/models/keras/resnet.py +52 -52
  70. stouputils/data_science/models/keras/squeezenet.py +233 -233
  71. stouputils/data_science/models/keras/vgg.py +42 -42
  72. stouputils/data_science/models/keras/xception.py +38 -38
  73. stouputils/data_science/models/keras_utils/callbacks/__init__.py +20 -20
  74. stouputils/data_science/models/keras_utils/callbacks/colored_progress_bar.py +219 -219
  75. stouputils/data_science/models/keras_utils/callbacks/learning_rate_finder.py +148 -148
  76. stouputils/data_science/models/keras_utils/callbacks/model_checkpoint_v2.py +31 -31
  77. stouputils/data_science/models/keras_utils/callbacks/progressive_unfreezing.py +249 -249
  78. stouputils/data_science/models/keras_utils/callbacks/warmup_scheduler.py +66 -66
  79. stouputils/data_science/models/keras_utils/losses/__init__.py +12 -12
  80. stouputils/data_science/models/keras_utils/losses/next_generation_loss.py +56 -56
  81. stouputils/data_science/models/keras_utils/visualizations.py +416 -416
  82. stouputils/data_science/models/sandbox.py +116 -116
  83. stouputils/data_science/range_tuple.py +234 -234
  84. stouputils/data_science/utils.py +285 -285
  85. stouputils/decorators.pyi +242 -0
  86. stouputils/image.pyi +172 -0
  87. stouputils/installer/__init__.py +18 -18
  88. stouputils/installer/__init__.pyi +5 -0
  89. stouputils/installer/common.pyi +39 -0
  90. stouputils/installer/downloader.pyi +24 -0
  91. stouputils/installer/linux.py +144 -144
  92. stouputils/installer/linux.pyi +39 -0
  93. stouputils/installer/main.py +223 -223
  94. stouputils/installer/main.pyi +57 -0
  95. stouputils/installer/windows.py +136 -136
  96. stouputils/installer/windows.pyi +31 -0
  97. stouputils/io.pyi +213 -0
  98. stouputils/parallel.py +12 -10
  99. stouputils/parallel.pyi +211 -0
  100. stouputils/print.pyi +136 -0
  101. stouputils/py.typed +1 -1
  102. stouputils/stouputils/parallel.pyi +4 -4
  103. stouputils/version_pkg.pyi +15 -0
  104. {stouputils-1.14.0.dist-info → stouputils-1.14.2.dist-info}/METADATA +1 -1
  105. stouputils-1.14.2.dist-info/RECORD +171 -0
  106. stouputils-1.14.0.dist-info/RECORD +0 -140
  107. {stouputils-1.14.0.dist-info → stouputils-1.14.2.dist-info}/WHEEL +0 -0
  108. {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
+