stouputils 1.14.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.
Files changed (140) hide show
  1. stouputils/__init__.py +40 -0
  2. stouputils/__main__.py +86 -0
  3. stouputils/_deprecated.py +37 -0
  4. stouputils/all_doctests.py +160 -0
  5. stouputils/applications/__init__.py +22 -0
  6. stouputils/applications/automatic_docs.py +634 -0
  7. stouputils/applications/upscaler/__init__.py +39 -0
  8. stouputils/applications/upscaler/config.py +128 -0
  9. stouputils/applications/upscaler/image.py +247 -0
  10. stouputils/applications/upscaler/video.py +287 -0
  11. stouputils/archive.py +344 -0
  12. stouputils/backup.py +488 -0
  13. stouputils/collections.py +244 -0
  14. stouputils/continuous_delivery/__init__.py +27 -0
  15. stouputils/continuous_delivery/cd_utils.py +243 -0
  16. stouputils/continuous_delivery/github.py +522 -0
  17. stouputils/continuous_delivery/pypi.py +130 -0
  18. stouputils/continuous_delivery/pyproject.py +147 -0
  19. stouputils/continuous_delivery/stubs.py +86 -0
  20. stouputils/ctx.py +408 -0
  21. stouputils/data_science/config/get.py +51 -0
  22. stouputils/data_science/config/set.py +125 -0
  23. stouputils/data_science/data_processing/image/__init__.py +66 -0
  24. stouputils/data_science/data_processing/image/auto_contrast.py +79 -0
  25. stouputils/data_science/data_processing/image/axis_flip.py +58 -0
  26. stouputils/data_science/data_processing/image/bias_field_correction.py +74 -0
  27. stouputils/data_science/data_processing/image/binary_threshold.py +73 -0
  28. stouputils/data_science/data_processing/image/blur.py +59 -0
  29. stouputils/data_science/data_processing/image/brightness.py +54 -0
  30. stouputils/data_science/data_processing/image/canny.py +110 -0
  31. stouputils/data_science/data_processing/image/clahe.py +92 -0
  32. stouputils/data_science/data_processing/image/common.py +30 -0
  33. stouputils/data_science/data_processing/image/contrast.py +53 -0
  34. stouputils/data_science/data_processing/image/curvature_flow_filter.py +74 -0
  35. stouputils/data_science/data_processing/image/denoise.py +378 -0
  36. stouputils/data_science/data_processing/image/histogram_equalization.py +123 -0
  37. stouputils/data_science/data_processing/image/invert.py +64 -0
  38. stouputils/data_science/data_processing/image/laplacian.py +60 -0
  39. stouputils/data_science/data_processing/image/median_blur.py +52 -0
  40. stouputils/data_science/data_processing/image/noise.py +59 -0
  41. stouputils/data_science/data_processing/image/normalize.py +65 -0
  42. stouputils/data_science/data_processing/image/random_erase.py +66 -0
  43. stouputils/data_science/data_processing/image/resize.py +69 -0
  44. stouputils/data_science/data_processing/image/rotation.py +80 -0
  45. stouputils/data_science/data_processing/image/salt_pepper.py +68 -0
  46. stouputils/data_science/data_processing/image/sharpening.py +55 -0
  47. stouputils/data_science/data_processing/image/shearing.py +64 -0
  48. stouputils/data_science/data_processing/image/threshold.py +64 -0
  49. stouputils/data_science/data_processing/image/translation.py +71 -0
  50. stouputils/data_science/data_processing/image/zoom.py +83 -0
  51. stouputils/data_science/data_processing/image_augmentation.py +118 -0
  52. stouputils/data_science/data_processing/image_preprocess.py +183 -0
  53. stouputils/data_science/data_processing/prosthesis_detection.py +359 -0
  54. stouputils/data_science/data_processing/technique.py +481 -0
  55. stouputils/data_science/dataset/__init__.py +45 -0
  56. stouputils/data_science/dataset/dataset.py +292 -0
  57. stouputils/data_science/dataset/dataset_loader.py +135 -0
  58. stouputils/data_science/dataset/grouping_strategy.py +296 -0
  59. stouputils/data_science/dataset/image_loader.py +100 -0
  60. stouputils/data_science/dataset/xy_tuple.py +696 -0
  61. stouputils/data_science/metric_dictionnary.py +106 -0
  62. stouputils/data_science/metric_utils.py +847 -0
  63. stouputils/data_science/mlflow_utils.py +206 -0
  64. stouputils/data_science/models/abstract_model.py +149 -0
  65. stouputils/data_science/models/all.py +85 -0
  66. stouputils/data_science/models/base_keras.py +765 -0
  67. stouputils/data_science/models/keras/all.py +38 -0
  68. stouputils/data_science/models/keras/convnext.py +62 -0
  69. stouputils/data_science/models/keras/densenet.py +50 -0
  70. stouputils/data_science/models/keras/efficientnet.py +60 -0
  71. stouputils/data_science/models/keras/mobilenet.py +56 -0
  72. stouputils/data_science/models/keras/resnet.py +52 -0
  73. stouputils/data_science/models/keras/squeezenet.py +233 -0
  74. stouputils/data_science/models/keras/vgg.py +42 -0
  75. stouputils/data_science/models/keras/xception.py +38 -0
  76. stouputils/data_science/models/keras_utils/callbacks/__init__.py +20 -0
  77. stouputils/data_science/models/keras_utils/callbacks/colored_progress_bar.py +219 -0
  78. stouputils/data_science/models/keras_utils/callbacks/learning_rate_finder.py +148 -0
  79. stouputils/data_science/models/keras_utils/callbacks/model_checkpoint_v2.py +31 -0
  80. stouputils/data_science/models/keras_utils/callbacks/progressive_unfreezing.py +249 -0
  81. stouputils/data_science/models/keras_utils/callbacks/warmup_scheduler.py +66 -0
  82. stouputils/data_science/models/keras_utils/losses/__init__.py +12 -0
  83. stouputils/data_science/models/keras_utils/losses/next_generation_loss.py +56 -0
  84. stouputils/data_science/models/keras_utils/visualizations.py +416 -0
  85. stouputils/data_science/models/model_interface.py +939 -0
  86. stouputils/data_science/models/sandbox.py +116 -0
  87. stouputils/data_science/range_tuple.py +234 -0
  88. stouputils/data_science/scripts/augment_dataset.py +77 -0
  89. stouputils/data_science/scripts/exhaustive_process.py +133 -0
  90. stouputils/data_science/scripts/preprocess_dataset.py +70 -0
  91. stouputils/data_science/scripts/routine.py +168 -0
  92. stouputils/data_science/utils.py +285 -0
  93. stouputils/decorators.py +605 -0
  94. stouputils/image.py +441 -0
  95. stouputils/installer/__init__.py +18 -0
  96. stouputils/installer/common.py +67 -0
  97. stouputils/installer/downloader.py +101 -0
  98. stouputils/installer/linux.py +144 -0
  99. stouputils/installer/main.py +223 -0
  100. stouputils/installer/windows.py +136 -0
  101. stouputils/io.py +486 -0
  102. stouputils/parallel.py +483 -0
  103. stouputils/print.py +482 -0
  104. stouputils/py.typed +1 -0
  105. stouputils/stouputils/__init__.pyi +15 -0
  106. stouputils/stouputils/_deprecated.pyi +12 -0
  107. stouputils/stouputils/all_doctests.pyi +46 -0
  108. stouputils/stouputils/applications/__init__.pyi +2 -0
  109. stouputils/stouputils/applications/automatic_docs.pyi +106 -0
  110. stouputils/stouputils/applications/upscaler/__init__.pyi +3 -0
  111. stouputils/stouputils/applications/upscaler/config.pyi +18 -0
  112. stouputils/stouputils/applications/upscaler/image.pyi +109 -0
  113. stouputils/stouputils/applications/upscaler/video.pyi +60 -0
  114. stouputils/stouputils/archive.pyi +67 -0
  115. stouputils/stouputils/backup.pyi +109 -0
  116. stouputils/stouputils/collections.pyi +86 -0
  117. stouputils/stouputils/continuous_delivery/__init__.pyi +5 -0
  118. stouputils/stouputils/continuous_delivery/cd_utils.pyi +129 -0
  119. stouputils/stouputils/continuous_delivery/github.pyi +162 -0
  120. stouputils/stouputils/continuous_delivery/pypi.pyi +53 -0
  121. stouputils/stouputils/continuous_delivery/pyproject.pyi +67 -0
  122. stouputils/stouputils/continuous_delivery/stubs.pyi +39 -0
  123. stouputils/stouputils/ctx.pyi +211 -0
  124. stouputils/stouputils/decorators.pyi +252 -0
  125. stouputils/stouputils/image.pyi +172 -0
  126. stouputils/stouputils/installer/__init__.pyi +5 -0
  127. stouputils/stouputils/installer/common.pyi +39 -0
  128. stouputils/stouputils/installer/downloader.pyi +24 -0
  129. stouputils/stouputils/installer/linux.pyi +39 -0
  130. stouputils/stouputils/installer/main.pyi +57 -0
  131. stouputils/stouputils/installer/windows.pyi +31 -0
  132. stouputils/stouputils/io.pyi +213 -0
  133. stouputils/stouputils/parallel.pyi +216 -0
  134. stouputils/stouputils/print.pyi +136 -0
  135. stouputils/stouputils/version_pkg.pyi +15 -0
  136. stouputils/version_pkg.py +189 -0
  137. stouputils-1.14.0.dist-info/METADATA +178 -0
  138. stouputils-1.14.0.dist-info/RECORD +140 -0
  139. stouputils-1.14.0.dist-info/WHEEL +4 -0
  140. stouputils-1.14.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,481 @@
1
+
2
+ # pyright: reportIncompatibleMethodOverride=false
3
+
4
+ # Imports
5
+ from __future__ import annotations
6
+
7
+ import random
8
+ from collections.abc import Callable, Iterable
9
+ from enum import Enum
10
+ from typing import Any, Literal, NamedTuple
11
+
12
+ import cv2
13
+ from numpy.typing import NDArray
14
+ from PIL import Image
15
+
16
+ from ..range_tuple import RangeTuple
17
+
18
+ # Import folders
19
+ from .image import (
20
+ adaptive_denoise_image,
21
+ auto_contrast_image,
22
+ bias_field_correction_image,
23
+ bilateral_denoise_image,
24
+ binary_threshold_image,
25
+ blur_image,
26
+ brightness_image,
27
+ canny_image,
28
+ clahe_image,
29
+ contrast_image,
30
+ curvature_flow_filter_image,
31
+ flip_image,
32
+ invert_image,
33
+ laplacian_image,
34
+ median_blur_image,
35
+ nlm_denoise_image,
36
+ noise_image,
37
+ normalize_image,
38
+ random_erase_image,
39
+ resize_image,
40
+ rotate_image,
41
+ salt_pepper_image,
42
+ sharpen_image,
43
+ shear_image,
44
+ threshold_image,
45
+ translate_image,
46
+ tv_denoise_image,
47
+ wavelet_denoise_image,
48
+ zoom_image,
49
+ )
50
+
51
+
52
+ # Tuple class
53
+ class ProcessingTechnique(NamedTuple):
54
+ """ A named tuple containing an processing technique.
55
+
56
+ The following descriptions are the recommendations:
57
+
58
+ - rotation: Rotation between -20° and +20° is generally safe
59
+ - translation: Translation up to 15% to avoid position bias
60
+ - shearing: Shearing up to 15° on x and y axes
61
+ - noise: Gaussian noise with fixed values [0.3,0.4,0.5]
62
+ - salt-pepper: Salt-pepper noise with densities [0.01,0.02,0.03]
63
+ - sharpening: Gaussian blur (variance=1) then subtract from original
64
+ - contrast: Linear scaling between min/max intensities [0.7,1.3]
65
+ - axis_flip: Flip along x and/or y axis (0 for x, 1 for y, 2 for both), probability=0.5
66
+ - zoom: Magnification/zoom between 85% and 115%
67
+ - brightness: Adjust overall image brightness [0.7,1.3]
68
+ - blur: Gaussian blur for reduced sharpness [0.5,2.0]
69
+ - random_erase: Randomly erase rectangles (1-10% of image) to learn descriptive features
70
+ - clahe: Contrast Limited Adaptive Histogram Equalization (clip_limit=[1.0,4.0], tile_size=8)
71
+ - binary_threshold: Binary thresholding with threshold [0.1,0.9]
72
+ - threshold: Multi-level thresholding with levels [0.3,0.6]
73
+ - canny: Canny edge detection (thresholds=[50/255,150/255], aperture=3)
74
+ - laplacian: Laplacian edge detection with 3x3 kernel
75
+ - auto_contrast: Auto contrast the image
76
+ - bias_field_correction: Bias field correction
77
+ - curvature_flow_filter: Curvature flow filter (time_step=0.05, iterations=5)
78
+ - median_blur: Median blur (kernel_size=15, iterations=10)
79
+ - resize: Resize the image (width=224, height=224, resample=Image.Resampling.LANCZOS)
80
+ - normalize: Normalize the image (min=0, max=255, method=cv2.NORM_MINMAX)
81
+ - nlm_denoise: Non-local means denoising (h=10, template_window_size=7, search_window_size=21)
82
+ - bilateral_denoise: Bilateral filter denoising (d=9, sigma_color=75, sigma_space=75)
83
+ - tv_denoise: Total variation denoising (weight=0.1, iterations=30)
84
+ - wavelet_denoise: Wavelet denoising (wavelet='db1', mode='soft', wavelet_levels=3)
85
+ - adaptive_denoise: Adaptive denoising (method="nlm", strength=0.5)
86
+ - custom: Custom processing technique (callable) # Ex: ProcessingTechnique("custom", custom=f)
87
+ """
88
+ # The following descriptions are the recommendations
89
+ name: Literal[
90
+ "rotation", "translation", "shearing", "noise", "salt-pepper", "sharpening",
91
+ "contrast", "axis_flip", "zoom", "brightness", "blur", "random_erase", "clahe",
92
+ "binary_threshold", "threshold", "canny", "laplacian", "auto_contrast",
93
+ "bias_field_correction", "curvature_flow_filter", "resize",
94
+ "normalize", "median_blur", "nlm_denoise", "bilateral_denoise",
95
+ "tv_denoise", "wavelet_denoise", "adaptive_denoise", "invert", "custom"
96
+ ]
97
+ """ Name of the processing technique """
98
+ ranges: list[Iterable[Any] | RangeTuple]
99
+ """ List of ranges for the processing technique.
100
+ Depending on the technique, multiple ranges might be needed.
101
+ Ex: Translation (x and y axes) or Shearing (x and y axes). """
102
+ probability: float = 1.0
103
+ """ Probability of applying the processing technique (default: 1.0).
104
+ Should be used on techniques like "axis_flip" or "random_erase"
105
+ where the probability of applying the technique is not 100%. """
106
+ custom: Callable[..., NDArray[Any]] | None = None
107
+ """ Custom processing technique (callable), name must be "custom", e.g. ProcessingTechnique("custom", custom=f) """
108
+
109
+ def __str__(self) -> str:
110
+ return (
111
+ f"name={self.name}, ranges={self.ranges}, probability={self.probability}, "
112
+ f"custom={self.custom.__name__ if self.custom else None}"
113
+ )
114
+
115
+ def __repr__(self) -> str:
116
+ return (
117
+ f"ProcessingTechnique(name={self.name!r}, ranges={self.ranges!r}, "
118
+ f"probability={self.probability}, custom={self.custom.__name__ if self.custom else None}"
119
+ )
120
+
121
+ def __mul__(self, other: float) -> ProcessingTechnique:
122
+ """ Multiply all ranges by a scalar value.
123
+
124
+ Args:
125
+ other (float): Value to multiply ranges by
126
+ Returns:
127
+ ProcessingTechnique: New object with scaled ranges
128
+ """
129
+ return ProcessingTechnique(
130
+ self.name,
131
+ [
132
+ (r * other) if isinstance(r, RangeTuple) else [x * other if isinstance(x, float | int) else x for x in r]
133
+ for r in self.ranges
134
+ ],
135
+ probability=self.probability,
136
+ custom=self.custom
137
+ )
138
+
139
+ def __truediv__(self, other: float) -> ProcessingTechnique:
140
+ """ Divide all ranges by a scalar value.
141
+
142
+ Args:
143
+ other (float): Value to divide ranges by
144
+ Returns:
145
+ ProcessingTechnique: New object with scaled ranges
146
+ """
147
+ return ProcessingTechnique(
148
+ self.name,
149
+ [
150
+ (r / other) if isinstance(r, RangeTuple) else [x / other if isinstance(x, float | int) else x for x in r]
151
+ for r in self.ranges
152
+ ],
153
+ probability=self.probability,
154
+ custom=self.custom
155
+ )
156
+
157
+ def deterministic(self, use_default: bool = False) -> ProcessingTechnique:
158
+ """ Convert the RangeTuple to values by calling the RangeTuple.random() method. """
159
+ # Make values deterministic
160
+ values: list[Iterable[Any]] = []
161
+ for range in self.ranges:
162
+ if isinstance(range, RangeTuple):
163
+ if use_default:
164
+ values.append([range.default])
165
+ else:
166
+ values.append([range.random()])
167
+ else:
168
+ values.append(range)
169
+
170
+ # Make probability deterministic (0 or 1)
171
+ probability: float = 1.0 if random.random() < self.probability else 0.0
172
+ return ProcessingTechnique(self.name, values, probability=probability, custom=self.custom)
173
+
174
+ def apply(self, image: NDArray[Any], dividers: tuple[float, float] = (1.0, 1.0), times: int = 1) -> NDArray[Any]:
175
+ """ Apply the processing technique to the image.
176
+
177
+ Args:
178
+ image (NDArray[Any]): Image to apply the processing technique to
179
+ dividers (tuple[float, float]): Dividers used to adjust the processing technique parameters (default: (1.0, 1.0))
180
+ times (int): Number of times to apply the processing technique (default: 1)
181
+ Returns:
182
+ NDArray[Any]: Processed image
183
+ """
184
+ assert not any(isinstance(x, RangeTuple) for x in self.ranges), (
185
+ "All RangeTuples must be converted to values, "
186
+ f"please call deterministic() before. {self.ranges=}"
187
+ )
188
+
189
+ # Check if the technique is applied
190
+ if random.random() > self.probability:
191
+ return image
192
+
193
+ for _ in range(times):
194
+
195
+ # Apply the processing technique
196
+ if self.name == "rotation":
197
+ angle: float = next(iter(self.ranges[0]))
198
+ image = rotate_image(image, angle)
199
+
200
+ elif self.name == "translation":
201
+ x_shift: float = next(iter(self.ranges[0])) / dividers[0]
202
+ y_shift: float = next(iter(self.ranges[1])) / dividers[1]
203
+ image = translate_image(image, x_shift, y_shift)
204
+
205
+ elif self.name == "shearing":
206
+ x_shear: float = next(iter(self.ranges[0])) / dividers[0]
207
+ y_shear: float = next(iter(self.ranges[1])) / dividers[1]
208
+ image = shear_image(image, x_shear, y_shear)
209
+
210
+ elif self.name == "axis_flip":
211
+ axis: Literal["horizontal", "vertical", "both"] = next(iter(self.ranges[0]))
212
+ image = flip_image(image, axis)
213
+
214
+ elif self.name == "noise":
215
+ intensity: float = next(iter(self.ranges[0]))
216
+ image = noise_image(image, intensity)
217
+
218
+ elif self.name == "salt-pepper":
219
+ density: float = next(iter(self.ranges[0]))
220
+ image = salt_pepper_image(image, density)
221
+
222
+ elif self.name == "sharpening":
223
+ alpha: float = next(iter(self.ranges[0]))
224
+ image = sharpen_image(image, alpha)
225
+
226
+ elif self.name == "contrast":
227
+ factor: float = next(iter(self.ranges[0]))
228
+ image = contrast_image(image, factor)
229
+
230
+ elif self.name == "zoom":
231
+ zoom_factor: float = next(iter(self.ranges[0])) / max(dividers)
232
+ image = zoom_image(image, zoom_factor)
233
+
234
+ elif self.name == "brightness":
235
+ brightness_factor: float = next(iter(self.ranges[0]))
236
+ image = brightness_image(image, brightness_factor)
237
+
238
+ elif self.name == "blur":
239
+ sigma: float = next(iter(self.ranges[0]))
240
+ image = blur_image(image, sigma)
241
+
242
+ elif self.name == "random_erase":
243
+ ratio: float = next(iter(self.ranges[0])) / max(dividers)
244
+ image = random_erase_image(image, ratio)
245
+
246
+ elif self.name == "clahe":
247
+ clip_limit: float = next(iter(self.ranges[0]))
248
+ tile_grid_size: int = int(next(iter(self.ranges[0])))
249
+ image = clahe_image(image, clip_limit, tile_grid_size)
250
+
251
+ elif self.name == "binary_threshold":
252
+ threshold: float = next(iter(self.ranges[0]))
253
+ image = binary_threshold_image(image, threshold)
254
+
255
+ elif self.name == "threshold":
256
+ thresholds: list[float] = [next(iter(r)) for r in self.ranges]
257
+ image = threshold_image(image, thresholds)
258
+
259
+ elif self.name == "canny":
260
+ threshold1: float = next(iter(self.ranges[0]))
261
+ threshold2: float = next(iter(self.ranges[1]))
262
+ aperture_size: int = int(next(iter(self.ranges[2]))) if len(self.ranges) > 2 else 3
263
+ image = canny_image(image, threshold1, threshold2, aperture_size)
264
+
265
+ elif self.name == "laplacian":
266
+ kernel_size: int = int(next(iter(self.ranges[0]))) if self.ranges else 3
267
+ image = laplacian_image(image, kernel_size)
268
+
269
+ elif self.name == "auto_contrast":
270
+ image = auto_contrast_image(image)
271
+
272
+ elif self.name == "curvature_flow_filter":
273
+ time_step: float = next(iter(self.ranges[0]))
274
+ number_of_iterations: int = int(next(iter(self.ranges[1])))
275
+ image = curvature_flow_filter_image(image, time_step, number_of_iterations)
276
+
277
+ elif self.name == "bias_field_correction":
278
+ image = bias_field_correction_image(image)
279
+
280
+ elif self.name == "resize":
281
+ width: int = int(next(iter(self.ranges[0])))
282
+ height: int = int(next(iter(self.ranges[1])))
283
+ if len(self.ranges) > 2:
284
+ image = resize_image(image, width, height, Image.Resampling(next(iter(self.ranges[2]))))
285
+ else:
286
+ image = resize_image(image, width, height)
287
+
288
+ elif self.name == "normalize":
289
+ mini: float | int = next(iter(self.ranges[0]))
290
+ maxi: float | int = next(iter(self.ranges[1]))
291
+ norm_method: int = int(next(iter(self.ranges[2])))
292
+ image = normalize_image(image, mini, maxi, norm_method)
293
+
294
+ elif self.name == "median_blur":
295
+ kernel_size: int = int(next(iter(self.ranges[0])))
296
+ iterations: int = int(next(iter(self.ranges[1])))
297
+ image = median_blur_image(image, kernel_size, iterations)
298
+
299
+ elif self.name == "nlm_denoise":
300
+ h: float = float(next(iter(self.ranges[0])))
301
+ template_window_size: int = int(next(iter(self.ranges[1]))) if len(self.ranges) > 1 else 7
302
+ search_window_size: int = int(next(iter(self.ranges[2]))) if len(self.ranges) > 2 else 21
303
+ image = nlm_denoise_image(image, h, template_window_size, search_window_size)
304
+
305
+ elif self.name == "bilateral_denoise":
306
+ d: int = int(next(iter(self.ranges[0])))
307
+ sigma_color: float = float(next(iter(self.ranges[1]))) if len(self.ranges) > 1 else 75.0
308
+ sigma_space: float = float(next(iter(self.ranges[2]))) if len(self.ranges) > 2 else 75.0
309
+ image = bilateral_denoise_image(image, d, sigma_color, sigma_space)
310
+
311
+ elif self.name == "tv_denoise":
312
+ weight: float = float(next(iter(self.ranges[0])))
313
+ iterations: int = int(next(iter(self.ranges[1]))) if len(self.ranges) > 1 else 30
314
+ method_value = str(next(iter(self.ranges[2]))) if len(self.ranges) > 2 else "chambolle"
315
+ tv_method: Literal["chambolle", "bregman"] = "chambolle" if method_value == "chambolle" else "bregman"
316
+ image = tv_denoise_image(image, weight, iterations, tv_method)
317
+
318
+ elif self.name == "wavelet_denoise":
319
+ wavelet_levels: int = int(next(iter(self.ranges[0])))
320
+ wavelet_value = str(next(iter(self.ranges[1]))) if len(self.ranges) > 1 else "db1"
321
+ mode_value = str(next(iter(self.ranges[2]))) if len(self.ranges) > 2 else "soft"
322
+ # sigma is None by default in the function, so we don't provide it explicitly
323
+ image = wavelet_denoise_image(
324
+ image,
325
+ wavelet=wavelet_value,
326
+ mode=mode_value,
327
+ wavelet_levels=wavelet_levels
328
+ )
329
+
330
+ elif self.name == "adaptive_denoise":
331
+ method_value = str(next(iter(self.ranges[0])))
332
+ if len(self.ranges) > 1:
333
+ image = adaptive_denoise_image(image, method_value, float(next(iter(self.ranges[1]))))
334
+ else:
335
+ image = adaptive_denoise_image(image, method_value)
336
+
337
+ elif self.name == "invert":
338
+ image = invert_image(image)
339
+
340
+ elif self.name == "custom":
341
+ if self.custom is None:
342
+ raise ValueError(
343
+ "Custom processing technique is not defined, please set the custom attribute, "
344
+ "ex: ProcessingTechnique('custom', custom=f)"
345
+ )
346
+ args: list[Any] = [next(iter(r)) for r in self.ranges]
347
+ image = self.custom(image, *args)
348
+
349
+ else:
350
+ raise ValueError(f"Augmentation technique {self.name} is not supported.")
351
+
352
+ return image
353
+
354
+ def __call__(self, image: NDArray[Any], dividers: tuple[float, float] = (1.0, 1.0), times: int = 1) -> NDArray[Any]:
355
+ """ Apply the processing technique to the image.
356
+
357
+ Args:
358
+ image (NDArray[Any]): Image to apply the processing technique to
359
+ dividers (tuple[float, float]): Dividers used to adjust the processing technique parameters
360
+ (default: (1.0, 1.0))
361
+ times (int): Number of times to apply the processing technique
362
+ (default: 1)
363
+ Returns:
364
+ NDArray[Any]: Processed image
365
+ """
366
+ return self.apply(image, dividers, times)
367
+
368
+
369
+ # Recommendations enumerated
370
+ class RecommendedProcessingTechnique(Enum):
371
+ """ A class containing the processing techniques with their recommended ranges based on scientific papers. """
372
+
373
+ ROTATION = ProcessingTechnique("rotation", [RangeTuple(mini=-20, maxi=20, step=1, default=0)])
374
+ """ Rotation between -20° and +20° is generally safe for medical images """
375
+
376
+ TRANSLATION = ProcessingTechnique("translation", list(2 * [RangeTuple(mini=-0.15, maxi=0.15, step=0.01, default=0)]))
377
+ """ Translation between -15% and 15% of image size """
378
+
379
+ SHEARING = ProcessingTechnique("shearing", list(2 * [RangeTuple(mini=-10, maxi=10, step=1, default=0)]))
380
+ """ Shearing between -10% and 10% distortion """
381
+
382
+ AXIS_FLIP = ProcessingTechnique("axis_flip", [["horizontal"]], probability=0.5)
383
+ """ Flipping: horizontal is much more used than vertical """
384
+
385
+ NOISE = ProcessingTechnique("noise", [RangeTuple(mini=0.1, maxi=0.4, step=0.05, default=0.2)])
386
+ """ Noise: FV values between 0.1 and 0.4 for gaussian noise """
387
+
388
+ SALT_PEPPER = ProcessingTechnique("salt-pepper", [RangeTuple(mini=0.1, maxi=0.5, step=0.05, default=0.2)])
389
+ """ Salt-pepper: densities between 0.1 and 0.5 """
390
+
391
+ SHARPENING = ProcessingTechnique("sharpening", [RangeTuple(mini=0.5, maxi=1.5, step=0.1, default=1.1)])
392
+ """ Sharpening: gaussian blur with variance=1 then subtract from original """
393
+
394
+ CONTRAST = ProcessingTechnique("contrast", [RangeTuple(mini=0.7, maxi=1.3, step=0.1, default=1.1)])
395
+ """ Contrast: linear scaling between min and max intensity """
396
+
397
+ ZOOM = ProcessingTechnique("zoom", [RangeTuple(mini=0.85, maxi=1.15, step=0.05, default=1.05)])
398
+ """ Zoom (Magnification): between 85% and 115% """
399
+
400
+ BRIGHTNESS = ProcessingTechnique("brightness", [RangeTuple(mini=0.7, maxi=1.3, step=0.1, default=1.1)])
401
+ """ Brightness: moderate changes to avoid losing details """
402
+
403
+ BLUR = ProcessingTechnique("blur", [RangeTuple(mini=0.5, maxi=2, step=0.25, default=1)])
404
+ """ Blur: gaussian blur preferred, moderate values """
405
+
406
+ RANDOM_ERASE = ProcessingTechnique("random_erase", [RangeTuple(mini=0.01, maxi=0.1, step=0.01, default=0.05)])
407
+ """ Random erasing: small regions to force model to learn descriptive features """
408
+
409
+ CLAHE = ProcessingTechnique(
410
+ "clahe",
411
+ [
412
+ RangeTuple(mini=1.0, maxi=4.0, step=0.5, default=2.0), # clip_limit
413
+ [8] # tile_grid_size (fixed at 8x8)
414
+ ]
415
+ )
416
+ """ CLAHE: Contrast Limited Adaptive Histogram Equalization with recommended parameters """
417
+
418
+ BINARY_THRESHOLD = ProcessingTechnique("binary_threshold", [RangeTuple(mini=0.1, maxi=0.9, step=0.1, default=0.5)])
419
+ """ Binary threshold: threshold between 0.1 and 0.9 """
420
+
421
+ THRESHOLD = ProcessingTechnique("threshold", [[0.3], [0.6]])
422
+ """ Multi-level threshold: creates X levels of thresholding """
423
+
424
+ CANNY = ProcessingTechnique("canny", [[50 / 255], [150 / 255], [3]])
425
+ """ Canny edge detection with recommended parameters """
426
+
427
+ LAPLACIAN = ProcessingTechnique("laplacian", [[3]]) # kernel_size
428
+ """ Laplacian edge detection with 3x3 kernel """
429
+
430
+ AUTO_CONTRAST = ProcessingTechnique("auto_contrast", [])
431
+ """ Auto contrast the image """
432
+
433
+ CURVATURE_FLOW_FILTER = ProcessingTechnique("curvature_flow_filter", [[0.05], [5]])
434
+ """ Curvature flow filter with recommended parameters """
435
+
436
+ BIAS_FIELD_CORRECTION = ProcessingTechnique("bias_field_correction", [])
437
+ """ Bias field correction with recommended parameters """
438
+
439
+ NORMALIZE = ProcessingTechnique("normalize", [[0], [255], [cv2.NORM_MINMAX]])
440
+ """ Normalize the image to the range 0-255 """
441
+
442
+ MEDIAN_BLUR = ProcessingTechnique("median_blur", [[7], [1]])
443
+ """ Median blur with 7x7 kernel and 1 iteration """
444
+
445
+ NLM_DENOISE = ProcessingTechnique("nlm_denoise", [
446
+ RangeTuple(mini=5, maxi=20, step=1, default=10), # h: affects the strength of the denoising
447
+ [7], # template_window_size: size of the template window
448
+ [21] # search_window_size: size of the search window
449
+ ])
450
+ """ Non-local means denoising with recommended parameters """
451
+
452
+ BILATERAL_DENOISE = ProcessingTechnique("bilateral_denoise", [
453
+ [9], # diameter: size of the pixel neighborhood
454
+ RangeTuple(mini=30, maxi=150, step=10, default=75), # sigma_color: filter sigma in the color space
455
+ RangeTuple(mini=30, maxi=150, step=10, default=75) # sigma_space: filter sigma in the coordinate space
456
+ ])
457
+ """ Bilateral filter denoising with recommended parameters """
458
+
459
+ TV_DENOISE = ProcessingTechnique("tv_denoise", [
460
+ RangeTuple(mini=0.05, maxi=0.5, step=0.05, default=0.1), # weight: denoising weight
461
+ [30], # max_iter: maximum number of iterations
462
+ ["chambolle"] # method: algorithm used for denoising
463
+ ])
464
+ """ Total variation denoising with recommended parameters """
465
+
466
+ WAVELET_DENOISE = ProcessingTechnique("wavelet_denoise", [
467
+ [3], # wavelet_levels: number of wavelet decomposition levels
468
+ ["db1"], # wavelet: wavelet to use
469
+ ["soft"] # mode: thresholding mode
470
+ ])
471
+ """ Wavelet denoising with recommended parameters """
472
+
473
+ ADAPTIVE_DENOISE = ProcessingTechnique("adaptive_denoise", [
474
+ ["nlm"], # method: denoising method to use
475
+ RangeTuple(mini=0.1, maxi=0.9, step=0.1, default=0.5) # strength: denoising strength parameter
476
+ ])
477
+ """ Adaptive denoising with recommended parameters """
478
+
479
+ INVERT = ProcessingTechnique("invert", [], probability=0.5)
480
+ """ Invert the colors of an image with a 50% probability, hoping the model doesn't focus on bright parts only """
481
+
@@ -0,0 +1,45 @@
1
+ """ Package for advanced dataset handling.
2
+
3
+ Provides comprehensive tools for loading, processing and managing image datasets
4
+ with special handling for augmented data and group-aware operations.
5
+
6
+ Main Components:
7
+
8
+ - Dataset : Core class for storing and managing dataset splits with metadata
9
+ - DatasetLoader : Handles dataset loading from directories with various strategies
10
+ - DatasetSplitter : Manages stratified splitting while maintaining group integrity
11
+ - GroupingStrategy : Enum defining image grouping approaches (NONE/SIMPLE/CONCATENATE)
12
+ - XyTuple : Specialized container for features/labels with file tracking
13
+
14
+ Key Features:
15
+
16
+ - Augmented data handling with original file mapping
17
+ - Prevention of data leakage between train/test sets
18
+ - Support for multiple grouping strategies at subject/image level
19
+ - Class-aware dataset splitting with stratification
20
+ - Comprehensive metadata tracking (class distributions, file paths)
21
+ - Compatibility with keras.image_dataset_from_directory
22
+ - Group-aware k-fold cross validation support
23
+ """
24
+
25
+ # Imports
26
+ from .dataset import Dataset
27
+ from .dataset_loader import DatasetLoader
28
+ from .grouping_strategy import GroupingStrategy
29
+ from .image_loader import ALLOWLIST_FORMATS, load_images_from_directory
30
+ from .xy_tuple import XyTuple
31
+
32
+ # Constants
33
+ LOWER_GS: tuple[str, ...] = tuple(x.name.lower() for x in GroupingStrategy)
34
+
35
+ # Exports
36
+ __all__ = [
37
+ "ALLOWLIST_FORMATS",
38
+ "LOWER_GS",
39
+ "Dataset",
40
+ "DatasetLoader",
41
+ "GroupingStrategy",
42
+ "XyTuple",
43
+ "load_images_from_directory",
44
+ ]
45
+