nrtk-albumentations 2.1.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.

Potentially problematic release.


This version of nrtk-albumentations might be problematic. Click here for more details.

Files changed (62) hide show
  1. albumentations/__init__.py +21 -0
  2. albumentations/augmentations/__init__.py +23 -0
  3. albumentations/augmentations/blur/__init__.py +0 -0
  4. albumentations/augmentations/blur/functional.py +438 -0
  5. albumentations/augmentations/blur/transforms.py +1633 -0
  6. albumentations/augmentations/crops/__init__.py +0 -0
  7. albumentations/augmentations/crops/functional.py +494 -0
  8. albumentations/augmentations/crops/transforms.py +3647 -0
  9. albumentations/augmentations/dropout/__init__.py +0 -0
  10. albumentations/augmentations/dropout/channel_dropout.py +134 -0
  11. albumentations/augmentations/dropout/coarse_dropout.py +567 -0
  12. albumentations/augmentations/dropout/functional.py +1017 -0
  13. albumentations/augmentations/dropout/grid_dropout.py +166 -0
  14. albumentations/augmentations/dropout/mask_dropout.py +274 -0
  15. albumentations/augmentations/dropout/transforms.py +461 -0
  16. albumentations/augmentations/dropout/xy_masking.py +186 -0
  17. albumentations/augmentations/geometric/__init__.py +0 -0
  18. albumentations/augmentations/geometric/distortion.py +1238 -0
  19. albumentations/augmentations/geometric/flip.py +752 -0
  20. albumentations/augmentations/geometric/functional.py +4151 -0
  21. albumentations/augmentations/geometric/pad.py +676 -0
  22. albumentations/augmentations/geometric/resize.py +956 -0
  23. albumentations/augmentations/geometric/rotate.py +864 -0
  24. albumentations/augmentations/geometric/transforms.py +1962 -0
  25. albumentations/augmentations/mixing/__init__.py +0 -0
  26. albumentations/augmentations/mixing/domain_adaptation.py +787 -0
  27. albumentations/augmentations/mixing/domain_adaptation_functional.py +453 -0
  28. albumentations/augmentations/mixing/functional.py +878 -0
  29. albumentations/augmentations/mixing/transforms.py +832 -0
  30. albumentations/augmentations/other/__init__.py +0 -0
  31. albumentations/augmentations/other/lambda_transform.py +180 -0
  32. albumentations/augmentations/other/type_transform.py +261 -0
  33. albumentations/augmentations/pixel/__init__.py +0 -0
  34. albumentations/augmentations/pixel/functional.py +4226 -0
  35. albumentations/augmentations/pixel/transforms.py +7556 -0
  36. albumentations/augmentations/spectrogram/__init__.py +0 -0
  37. albumentations/augmentations/spectrogram/transform.py +220 -0
  38. albumentations/augmentations/text/__init__.py +0 -0
  39. albumentations/augmentations/text/functional.py +272 -0
  40. albumentations/augmentations/text/transforms.py +299 -0
  41. albumentations/augmentations/transforms3d/__init__.py +0 -0
  42. albumentations/augmentations/transforms3d/functional.py +393 -0
  43. albumentations/augmentations/transforms3d/transforms.py +1422 -0
  44. albumentations/augmentations/utils.py +249 -0
  45. albumentations/core/__init__.py +0 -0
  46. albumentations/core/bbox_utils.py +920 -0
  47. albumentations/core/composition.py +1885 -0
  48. albumentations/core/hub_mixin.py +299 -0
  49. albumentations/core/keypoints_utils.py +521 -0
  50. albumentations/core/label_manager.py +339 -0
  51. albumentations/core/pydantic.py +239 -0
  52. albumentations/core/serialization.py +352 -0
  53. albumentations/core/transforms_interface.py +976 -0
  54. albumentations/core/type_definitions.py +127 -0
  55. albumentations/core/utils.py +605 -0
  56. albumentations/core/validation.py +129 -0
  57. albumentations/pytorch/__init__.py +1 -0
  58. albumentations/pytorch/transforms.py +189 -0
  59. nrtk_albumentations-2.1.0.dist-info/METADATA +196 -0
  60. nrtk_albumentations-2.1.0.dist-info/RECORD +62 -0
  61. nrtk_albumentations-2.1.0.dist-info/WHEEL +4 -0
  62. nrtk_albumentations-2.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,21 @@
1
+ from importlib.metadata import metadata
2
+
3
+ try:
4
+ _metadata = metadata("albumentations")
5
+ __version__ = _metadata["Version"]
6
+ __author__ = _metadata["Author"]
7
+ __maintainer__ = _metadata["Maintainer"]
8
+ except Exception: # noqa: BLE001
9
+ __version__ = "unknown"
10
+ __author__ = "Vladimir Iglovikov"
11
+ __maintainer__ = "Kitware, Inc."
12
+
13
+ from contextlib import suppress
14
+
15
+ from .augmentations import *
16
+ from .core.composition import *
17
+ from .core.serialization import *
18
+ from .core.transforms_interface import *
19
+
20
+ with suppress(ImportError):
21
+ from .pytorch import *
@@ -0,0 +1,23 @@
1
+ from .blur.transforms import *
2
+ from .crops.transforms import *
3
+ from .dropout.channel_dropout import *
4
+ from .dropout.coarse_dropout import *
5
+ from .dropout.grid_dropout import *
6
+ from .dropout.mask_dropout import *
7
+ from .dropout.transforms import *
8
+ from .dropout.xy_masking import *
9
+ from .geometric.distortion import *
10
+ from .geometric.flip import *
11
+ from .geometric.pad import *
12
+ from .geometric.resize import *
13
+ from .geometric.rotate import *
14
+ from .geometric.transforms import *
15
+ from .mixing.domain_adaptation import *
16
+ from .mixing.transforms import *
17
+ from .other.lambda_transform import *
18
+ from .other.type_transform import *
19
+ from .pixel.transforms import *
20
+ from .spectrogram.transform import *
21
+ from .text.transforms import *
22
+ from .transforms3d.transforms import *
23
+ from .utils import *
File without changes
@@ -0,0 +1,438 @@
1
+ """Functional implementations of various blur operations for image processing.
2
+
3
+ This module provides a collection of low-level functions for applying different blur effects
4
+ to images, including standard blur, median blur, glass blur, defocus, and zoom effects.
5
+ These functions form the foundation for the corresponding transform classes.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import random
11
+ from collections.abc import Sequence
12
+ from itertools import product
13
+ from math import ceil
14
+ from typing import Literal
15
+ from warnings import warn
16
+
17
+ import cv2
18
+ import numpy as np
19
+ from albucore import clipped, float32_io, maybe_process_in_chunks, preserve_channel_dim, uint8_io
20
+ from pydantic import ValidationInfo
21
+
22
+ from albumentations.augmentations.geometric.functional import scale
23
+ from albumentations.augmentations.pixel.functional import convolve
24
+ from albumentations.core.type_definitions import EIGHT
25
+
26
+ __all__ = ["box_blur", "central_zoom", "defocus", "glass_blur", "median_blur", "zoom_blur"]
27
+
28
+
29
+ @preserve_channel_dim
30
+ def box_blur(img: np.ndarray, ksize: int) -> np.ndarray:
31
+ """Blur an image.
32
+
33
+ This function applies a blur to an image.
34
+
35
+ Args:
36
+ img (np.ndarray): Input image.
37
+ ksize (int): Kernel size.
38
+
39
+ Returns:
40
+ np.ndarray: Blurred image.
41
+
42
+ """
43
+ blur_fn = maybe_process_in_chunks(cv2.blur, ksize=(ksize, ksize))
44
+ return blur_fn(img)
45
+
46
+
47
+ @preserve_channel_dim
48
+ @uint8_io
49
+ def median_blur(img: np.ndarray, ksize: int) -> np.ndarray:
50
+ """Median blur an image.
51
+
52
+ This function applies a median blur to an image.
53
+
54
+ Args:
55
+ img (np.ndarray): Input image.
56
+ ksize (int): Kernel size.
57
+
58
+ Returns:
59
+ np.ndarray: Median blurred image.
60
+
61
+ """
62
+ blur_fn = maybe_process_in_chunks(cv2.medianBlur, ksize=ksize)
63
+ return blur_fn(img)
64
+
65
+
66
+ @preserve_channel_dim
67
+ def glass_blur(
68
+ img: np.ndarray,
69
+ sigma: float,
70
+ max_delta: int,
71
+ iterations: int,
72
+ dxy: np.ndarray,
73
+ mode: Literal["fast", "exact"],
74
+ ) -> np.ndarray:
75
+ """Glass blur an image.
76
+
77
+ This function applies a glass blur to an image.
78
+
79
+ Args:
80
+ img (np.ndarray): Input image.
81
+ sigma (float): Sigma.
82
+ max_delta (int): Maximum delta.
83
+ iterations (int): Number of iterations.
84
+ dxy (np.ndarray): Dxy.
85
+ mode (Literal["fast", "exact"]): Mode.
86
+
87
+ Returns:
88
+ np.ndarray: Glass blurred image.
89
+
90
+ """
91
+ x = cv2.GaussianBlur(np.array(img), sigmaX=sigma, ksize=(0, 0))
92
+
93
+ if mode == "fast":
94
+ hs = np.arange(img.shape[0] - max_delta, max_delta, -1)
95
+ ws = np.arange(img.shape[1] - max_delta, max_delta, -1)
96
+ h: int | np.ndarray = np.tile(hs, ws.shape[0])
97
+ w: int | np.ndarray = np.repeat(ws, hs.shape[0])
98
+
99
+ for i in range(iterations):
100
+ dy = dxy[:, i, 0]
101
+ dx = dxy[:, i, 1]
102
+ x[h, w], x[h + dy, w + dx] = x[h + dy, w + dx], x[h, w]
103
+
104
+ elif mode == "exact":
105
+ for ind, (i, h, w) in enumerate(
106
+ product(
107
+ range(iterations),
108
+ range(img.shape[0] - max_delta, max_delta, -1),
109
+ range(img.shape[1] - max_delta, max_delta, -1),
110
+ ),
111
+ ):
112
+ idx = ind if ind < len(dxy) else ind % len(dxy)
113
+ dy = dxy[idx, i, 0]
114
+ dx = dxy[idx, i, 1]
115
+ x[h, w], x[h + dy, w + dx] = x[h + dy, w + dx], x[h, w]
116
+ else:
117
+ raise ValueError(f"Unsupported mode `{mode}`. Supports only `fast` and `exact`.")
118
+
119
+ return cv2.GaussianBlur(x, sigmaX=sigma, ksize=(0, 0))
120
+
121
+
122
+ def defocus(img: np.ndarray, radius: int, alias_blur: float) -> np.ndarray:
123
+ """Defocus an image.
124
+
125
+ This function defocuses an image.
126
+
127
+ Args:
128
+ img (np.ndarray): Input image.
129
+ radius (int): Radius.
130
+ alias_blur (float): Alias blur.
131
+
132
+ Returns:
133
+ np.ndarray: Defocused image.
134
+
135
+ """
136
+ length = np.arange(-max(8, radius), max(8, radius) + 1)
137
+ ksize = 3 if radius <= EIGHT else 5
138
+
139
+ x, y = np.meshgrid(length, length)
140
+ aliased_disk = np.array((x**2 + y**2) <= radius**2, dtype=np.float32)
141
+ aliased_disk /= np.sum(aliased_disk)
142
+
143
+ kernel = cv2.GaussianBlur(aliased_disk, (ksize, ksize), sigmaX=alias_blur)
144
+
145
+ return convolve(img, kernel=kernel)
146
+
147
+
148
+ def central_zoom(img: np.ndarray, zoom_factor: int) -> np.ndarray:
149
+ """Central zoom an image.
150
+
151
+ This function zooms an image.
152
+
153
+ Args:
154
+ img (np.ndarray): Input image.
155
+ zoom_factor (int): Zoom factor.
156
+
157
+ Returns:
158
+ np.ndarray: Zoomed image.
159
+
160
+ """
161
+ height, width = img.shape[:2]
162
+ h_ch, w_ch = ceil(height / zoom_factor), ceil(width / zoom_factor)
163
+ h_top, w_top = (height - h_ch) // 2, (width - w_ch) // 2
164
+
165
+ img = scale(img[h_top : h_top + h_ch, w_top : w_top + w_ch], zoom_factor, cv2.INTER_LINEAR)
166
+ h_trim_top, w_trim_top = (img.shape[0] - height) // 2, (img.shape[1] - width) // 2
167
+ return img[h_trim_top : h_trim_top + height, w_trim_top : w_trim_top + width]
168
+
169
+
170
+ @float32_io
171
+ @clipped
172
+ def zoom_blur(img: np.ndarray, zoom_factors: np.ndarray | Sequence[int]) -> np.ndarray:
173
+ """Zoom blur an image.
174
+
175
+ This function zooms and blurs an image.
176
+
177
+ Args:
178
+ img (np.ndarray): Input image.
179
+ zoom_factors (np.ndarray | Sequence[int]): Zoom factors.
180
+
181
+ Returns:
182
+ np.ndarray: Zoomed and blurred image.
183
+
184
+ """
185
+ out = np.zeros_like(img, dtype=np.float32)
186
+
187
+ for zoom_factor in zoom_factors:
188
+ out += central_zoom(img, zoom_factor)
189
+
190
+ return (img + out) / (len(zoom_factors) + 1)
191
+
192
+
193
+ def _ensure_min_value(result: tuple[int, int], min_value: int, field_name: str | None) -> tuple[int, int]:
194
+ if result[0] < min_value or result[1] < min_value:
195
+ new_result = (max(min_value, result[0]), max(min_value, result[1]))
196
+ warn(
197
+ f"{field_name}: Invalid kernel size range {result}. "
198
+ f"Values less than {min_value} are not allowed. "
199
+ f"Range automatically adjusted to {new_result}.",
200
+ UserWarning,
201
+ stacklevel=2,
202
+ )
203
+ return new_result
204
+ return result
205
+
206
+
207
+ def _ensure_odd_values(result: tuple[int, int], field_name: str | None = None) -> tuple[int, int]:
208
+ new_result = (
209
+ result[0] if result[0] == 0 or result[0] % 2 == 1 else result[0] + 1,
210
+ result[1] if result[1] == 0 or result[1] % 2 == 1 else result[1] + 1,
211
+ )
212
+ if new_result != result:
213
+ warn(
214
+ f"{field_name}: Non-zero kernel sizes must be odd. Range {result} automatically adjusted to {new_result}.",
215
+ UserWarning,
216
+ stacklevel=2,
217
+ )
218
+ return new_result
219
+
220
+
221
+ def process_blur_limit(value: int | tuple[int, int], info: ValidationInfo, min_value: int = 0) -> tuple[int, int]:
222
+ """Process blur limit to ensure valid kernel sizes."""
223
+ # Convert value to tuple[int, int]
224
+ if isinstance(value, Sequence):
225
+ if len(value) != 2:
226
+ raise ValueError("Sequence must contain exactly 2 elements")
227
+ result = (int(value[0]), int(value[1]))
228
+ else:
229
+ result = (min_value, int(value))
230
+
231
+ result = _ensure_min_value(result, min_value, info.field_name)
232
+ result = _ensure_odd_values(result, info.field_name)
233
+
234
+ if result[0] > result[1]:
235
+ final_result = (result[1], result[1])
236
+ warn(
237
+ f"{info.field_name}: Invalid range {result} (min > max). Range automatically adjusted to {final_result}.",
238
+ UserWarning,
239
+ stacklevel=2,
240
+ )
241
+ return final_result
242
+
243
+ return result
244
+
245
+
246
+ def create_motion_kernel(
247
+ kernel_size: int,
248
+ angle: float,
249
+ direction: float,
250
+ allow_shifted: bool,
251
+ random_state: random.Random,
252
+ ) -> np.ndarray:
253
+ """Create a motion blur kernel.
254
+
255
+ Args:
256
+ kernel_size (int): Size of the kernel (must be odd)
257
+ angle (float): Angle in degrees (counter-clockwise)
258
+ direction (float): Blur direction (-1.0 to 1.0)
259
+ allow_shifted (bool): Allow kernel to be randomly shifted from center
260
+ random_state (random.Random): Python's random.Random instance
261
+
262
+ Returns:
263
+ np.ndarray: Motion blur kernel
264
+
265
+ """
266
+ # Validate direction range to prevent unexpected interpolation results
267
+ direction = np.clip(direction, -1.0, 1.0)
268
+
269
+ kernel = np.zeros((kernel_size, kernel_size), dtype=np.float32)
270
+ center = kernel_size // 2
271
+
272
+ # Convert angle to radians
273
+ angle_rad = np.deg2rad(angle)
274
+
275
+ # Calculate direction vector
276
+ dx = np.cos(angle_rad)
277
+ dy = np.sin(angle_rad)
278
+
279
+ # Create line points with direction bias
280
+ line_length = kernel_size // 2
281
+
282
+ # Apply direction bias to control the distribution of blur
283
+ if direction < 0:
284
+ # Backward bias: interpolate between symmetric and backward-only
285
+ # direction = -1: only backward, direction = 0: symmetric
286
+ bias_factor = abs(direction)
287
+ t_start = float(-line_length)
288
+ t_end = line_length * (1 - bias_factor)
289
+ elif direction > 0:
290
+ # Forward bias: interpolate between symmetric and forward-only
291
+ # direction = 1: only forward, direction = 0: symmetric
292
+ bias_factor = direction
293
+ t_start = -line_length * (1 - bias_factor)
294
+ t_end = float(line_length)
295
+ else:
296
+ # Symmetric case (direction = 0)
297
+ t_start = float(-line_length)
298
+ t_end = float(line_length)
299
+
300
+ # Generate points along the biased line
301
+ t = np.linspace(t_start, t_end, kernel_size)
302
+
303
+ # Generate line coordinates
304
+ x = center + dx * t
305
+ y = center + dy * t
306
+
307
+ # Apply random shift if allowed
308
+ if allow_shifted:
309
+ shift_x = random_state.uniform(-1, 1) * line_length / 2
310
+ shift_y = random_state.uniform(-1, 1) * line_length / 2
311
+ x += shift_x
312
+ y += shift_y
313
+
314
+ # Round coordinates and clip to kernel bounds
315
+ x = np.clip(np.round(x), 0, kernel_size - 1).astype(int)
316
+ y = np.clip(np.round(y), 0, kernel_size - 1).astype(int)
317
+
318
+ # Keep only unique points to avoid multiple assignments
319
+ points = np.unique(np.column_stack([y, x]), axis=0)
320
+ kernel[points[:, 0], points[:, 1]] = 1
321
+
322
+ # Ensure at least one point is set
323
+ if not kernel.any():
324
+ kernel[center, center] = 1
325
+
326
+ return kernel
327
+
328
+
329
+ def sample_odd_from_range(random_state: random.Random, low: int, high: int) -> int:
330
+ """Sample an odd number from the range [low, high] (inclusive).
331
+
332
+ Args:
333
+ random_state (random.Random): instance of random.Random
334
+ low (int): lower bound (will be converted to nearest valid odd number)
335
+ high (int): upper bound (will be converted to nearest valid odd number)
336
+
337
+ Returns:
338
+ int: Randomly sampled odd number from the range
339
+
340
+ Note:
341
+ - Input values will be converted to nearest valid odd numbers:
342
+ * Values less than 3 will become 3
343
+ * Even values will be rounded up to next odd number
344
+ - After normalization, high must be >= low
345
+
346
+ """
347
+ # Normalize low value
348
+ low = max(3, low + (low % 2 == 0))
349
+ # Normalize high value
350
+ high = max(3, high + (high % 2 == 0))
351
+
352
+ # Ensure high >= low after normalization
353
+ high = max(high, low)
354
+
355
+ if low == high:
356
+ return low
357
+
358
+ # Calculate number of possible odd values
359
+ num_odd_values = (high - low) // 2 + 1
360
+ # Generate random index and convert to corresponding odd number
361
+ rand_idx = random_state.randint(0, num_odd_values - 1)
362
+ return low + (2 * rand_idx)
363
+
364
+
365
+ def create_gaussian_kernel(sigma: float, ksize: int = 0) -> np.ndarray:
366
+ """Create a Gaussian kernel following PIL's approach.
367
+
368
+ Args:
369
+ sigma (float): Standard deviation for Gaussian kernel.
370
+ ksize (int): Kernel size. If 0, size is computed as int(sigma * 3.5) * 2 + 1
371
+ to match PIL's implementation. Otherwise, must be positive and odd.
372
+
373
+ Returns:
374
+ np.ndarray: 2D normalized Gaussian kernel.
375
+
376
+ """
377
+ # PIL's kernel creation approach
378
+ size = int(sigma * 3.5) * 2 + 1 if ksize == 0 else ksize
379
+
380
+ # Ensure odd size
381
+ size = size + 1 if size % 2 == 0 else size
382
+
383
+ # Create x coordinates
384
+ x = np.linspace(-(size // 2), size // 2, size)
385
+
386
+ # Compute 1D kernel using vectorized operations
387
+ kernel_1d = np.exp(-0.5 * (x / sigma) ** 2)
388
+ kernel_1d = kernel_1d / kernel_1d.sum()
389
+
390
+ # Create 2D kernel
391
+ return kernel_1d[:, np.newaxis] @ kernel_1d[np.newaxis, :]
392
+
393
+
394
+ def create_gaussian_kernel_1d(sigma: float, ksize: int = 0) -> np.ndarray:
395
+ """Create a 1D Gaussian kernel following PIL's approach.
396
+
397
+ Args:
398
+ sigma (float): Standard deviation for Gaussian kernel.
399
+ ksize (int): Kernel size. If 0, size is computed as int(sigma * 3.5) * 2 + 1
400
+ to match PIL's implementation. Otherwise, must be positive and odd.
401
+
402
+ Returns:
403
+ np.ndarray: 1D normalized Gaussian kernel.
404
+
405
+ """
406
+ # PIL's kernel creation approach
407
+ size = int(sigma * 3.5) * 2 + 1 if ksize == 0 else ksize
408
+
409
+ # Ensure odd size
410
+ size = size + 1 if size % 2 == 0 else size
411
+
412
+ # Create x coordinates
413
+ x = create_gaussian_kernel_input_array(size=size)
414
+
415
+ # Compute 1D kernel using vectorized operations
416
+ kernel_1d = np.exp(-0.5 * (x / sigma) ** 2)
417
+ return kernel_1d / kernel_1d.sum()
418
+
419
+
420
+ def create_gaussian_kernel_input_array(size: int) -> np.ndarray:
421
+ """Creates a 1-D array which will create an array of x-coordinates which will be input for the
422
+ gaussian function (values from -size/2 to size/2 with step size of 1)
423
+
424
+ Piecewise function is needed as equivalent python list comprehension is faster than np.linspace
425
+ for values of size < 100
426
+
427
+ Args:
428
+ size (int): kernel size
429
+
430
+ Returns:
431
+ np.ndarray: x-coordinate array which will be input for gaussian function that will be used for
432
+ separable gaussian blur
433
+
434
+ """
435
+ if size < 100:
436
+ return np.array(list(range(-(size // 2), (size // 2) + 1, 1)))
437
+
438
+ return np.linspace(-(size // 2), size // 2, size)