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.
- albumentations/__init__.py +21 -0
- albumentations/augmentations/__init__.py +23 -0
- albumentations/augmentations/blur/__init__.py +0 -0
- albumentations/augmentations/blur/functional.py +438 -0
- albumentations/augmentations/blur/transforms.py +1633 -0
- albumentations/augmentations/crops/__init__.py +0 -0
- albumentations/augmentations/crops/functional.py +494 -0
- albumentations/augmentations/crops/transforms.py +3647 -0
- albumentations/augmentations/dropout/__init__.py +0 -0
- albumentations/augmentations/dropout/channel_dropout.py +134 -0
- albumentations/augmentations/dropout/coarse_dropout.py +567 -0
- albumentations/augmentations/dropout/functional.py +1017 -0
- albumentations/augmentations/dropout/grid_dropout.py +166 -0
- albumentations/augmentations/dropout/mask_dropout.py +274 -0
- albumentations/augmentations/dropout/transforms.py +461 -0
- albumentations/augmentations/dropout/xy_masking.py +186 -0
- albumentations/augmentations/geometric/__init__.py +0 -0
- albumentations/augmentations/geometric/distortion.py +1238 -0
- albumentations/augmentations/geometric/flip.py +752 -0
- albumentations/augmentations/geometric/functional.py +4151 -0
- albumentations/augmentations/geometric/pad.py +676 -0
- albumentations/augmentations/geometric/resize.py +956 -0
- albumentations/augmentations/geometric/rotate.py +864 -0
- albumentations/augmentations/geometric/transforms.py +1962 -0
- albumentations/augmentations/mixing/__init__.py +0 -0
- albumentations/augmentations/mixing/domain_adaptation.py +787 -0
- albumentations/augmentations/mixing/domain_adaptation_functional.py +453 -0
- albumentations/augmentations/mixing/functional.py +878 -0
- albumentations/augmentations/mixing/transforms.py +832 -0
- albumentations/augmentations/other/__init__.py +0 -0
- albumentations/augmentations/other/lambda_transform.py +180 -0
- albumentations/augmentations/other/type_transform.py +261 -0
- albumentations/augmentations/pixel/__init__.py +0 -0
- albumentations/augmentations/pixel/functional.py +4226 -0
- albumentations/augmentations/pixel/transforms.py +7556 -0
- albumentations/augmentations/spectrogram/__init__.py +0 -0
- albumentations/augmentations/spectrogram/transform.py +220 -0
- albumentations/augmentations/text/__init__.py +0 -0
- albumentations/augmentations/text/functional.py +272 -0
- albumentations/augmentations/text/transforms.py +299 -0
- albumentations/augmentations/transforms3d/__init__.py +0 -0
- albumentations/augmentations/transforms3d/functional.py +393 -0
- albumentations/augmentations/transforms3d/transforms.py +1422 -0
- albumentations/augmentations/utils.py +249 -0
- albumentations/core/__init__.py +0 -0
- albumentations/core/bbox_utils.py +920 -0
- albumentations/core/composition.py +1885 -0
- albumentations/core/hub_mixin.py +299 -0
- albumentations/core/keypoints_utils.py +521 -0
- albumentations/core/label_manager.py +339 -0
- albumentations/core/pydantic.py +239 -0
- albumentations/core/serialization.py +352 -0
- albumentations/core/transforms_interface.py +976 -0
- albumentations/core/type_definitions.py +127 -0
- albumentations/core/utils.py +605 -0
- albumentations/core/validation.py +129 -0
- albumentations/pytorch/__init__.py +1 -0
- albumentations/pytorch/transforms.py +189 -0
- nrtk_albumentations-2.1.0.dist-info/METADATA +196 -0
- nrtk_albumentations-2.1.0.dist-info/RECORD +62 -0
- nrtk_albumentations-2.1.0.dist-info/WHEEL +4 -0
- nrtk_albumentations-2.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"""Module containing utility functions for augmentation operations.
|
|
2
|
+
|
|
3
|
+
This module provides a collection of helper functions and utilities used throughout
|
|
4
|
+
the augmentation pipeline. It includes functions for image loading, type checking,
|
|
5
|
+
error handling, mathematical operations, and decorators that add functionality to
|
|
6
|
+
other functions in the codebase. These utilities help ensure consistent behavior
|
|
7
|
+
and simplify common operations across different augmentation transforms.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import functools
|
|
13
|
+
from functools import wraps
|
|
14
|
+
from typing import TYPE_CHECKING, Any, Callable, TypeVar, cast
|
|
15
|
+
|
|
16
|
+
import cv2
|
|
17
|
+
import numpy as np
|
|
18
|
+
from albucore.utils import (
|
|
19
|
+
is_grayscale_image,
|
|
20
|
+
is_multispectral_image,
|
|
21
|
+
is_rgb_image,
|
|
22
|
+
)
|
|
23
|
+
from typing_extensions import Concatenate, ParamSpec
|
|
24
|
+
|
|
25
|
+
from albumentations.core.keypoints_utils import angle_to_2pi_range
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"angle_2pi_range",
|
|
33
|
+
"non_rgb_error",
|
|
34
|
+
"read_bgr_image",
|
|
35
|
+
"read_grayscale",
|
|
36
|
+
"read_rgb_image",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
P = ParamSpec("P")
|
|
40
|
+
T = TypeVar("T", bound=np.ndarray)
|
|
41
|
+
F = TypeVar("F", bound=Callable[..., Any])
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def read_bgr_image(path: str | Path) -> np.ndarray:
|
|
45
|
+
"""Read an image in BGR format from the specified path.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
path (str | Path): Path to the image file.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
np.ndarray: Image in BGR format as a numpy array.
|
|
52
|
+
|
|
53
|
+
"""
|
|
54
|
+
return cv2.imread(str(path), cv2.IMREAD_COLOR)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def read_rgb_image(path: str | Path) -> np.ndarray:
|
|
58
|
+
"""Read an image in RGB format from the specified path.
|
|
59
|
+
|
|
60
|
+
This function reads an image in BGR format using OpenCV and then
|
|
61
|
+
converts it to RGB format.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
path (str | Path): Path to the image file.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
np.ndarray: Image in RGB format as a numpy array.
|
|
68
|
+
|
|
69
|
+
"""
|
|
70
|
+
image = read_bgr_image(path)
|
|
71
|
+
return cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def read_grayscale(path: str | Path) -> np.ndarray:
|
|
75
|
+
"""Read a grayscale image from the specified path.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
path (str | Path): Path to the image file.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
np.ndarray: Grayscale image as a numpy array.
|
|
82
|
+
|
|
83
|
+
"""
|
|
84
|
+
return cv2.imread(str(path), cv2.IMREAD_GRAYSCALE)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def angle_2pi_range(
|
|
88
|
+
func: Callable[Concatenate[np.ndarray, P], np.ndarray],
|
|
89
|
+
) -> Callable[Concatenate[np.ndarray, P], np.ndarray]:
|
|
90
|
+
"""Decorator to normalize angle values to the range [0, 2π).
|
|
91
|
+
|
|
92
|
+
This decorator wraps a function that processes keypoints, ensuring that
|
|
93
|
+
angle values (stored in the 4th column, index 3) are normalized to the
|
|
94
|
+
range [0, 2π) after the wrapped function executes.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
func (Callable): Function that processes keypoints and returns a numpy array.
|
|
98
|
+
The function should take a keypoints array as its first parameter.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Callable: Wrapped function that normalizes angles after processing keypoints.
|
|
102
|
+
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
@wraps(func)
|
|
106
|
+
def wrapped_function(keypoints: np.ndarray, *args: P.args, **kwargs: P.kwargs) -> np.ndarray:
|
|
107
|
+
result = func(keypoints, *args, **kwargs)
|
|
108
|
+
if len(result) > 0 and result.shape[1] > 3:
|
|
109
|
+
result[:, 3] = angle_to_2pi_range(result[:, 3])
|
|
110
|
+
return result
|
|
111
|
+
|
|
112
|
+
return wrapped_function
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def non_rgb_error(image: np.ndarray) -> None:
|
|
116
|
+
"""Check if the input image is RGB and raise a ValueError if it's not.
|
|
117
|
+
|
|
118
|
+
This function is used to ensure that certain transformations are only applied to
|
|
119
|
+
RGB images. It provides helpful error messages for grayscale and multi-spectral images.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
image (np.ndarray): The input image to check. Expected to be a numpy array
|
|
123
|
+
representing an image.
|
|
124
|
+
|
|
125
|
+
Raises:
|
|
126
|
+
ValueError: If the input image is not an RGB image (i.e., does not have exactly 3 channels).
|
|
127
|
+
The error message includes specific instructions for grayscale images
|
|
128
|
+
and a note about incompatibility with multi-spectral images.
|
|
129
|
+
|
|
130
|
+
Note:
|
|
131
|
+
- RGB images are expected to have exactly 3 channels.
|
|
132
|
+
- Grayscale images (1 channel) will trigger an error with conversion instructions.
|
|
133
|
+
- Multi-spectral images (more than 3 channels) will trigger an error stating incompatibility.
|
|
134
|
+
|
|
135
|
+
Examples:
|
|
136
|
+
>>> import numpy as np
|
|
137
|
+
>>> rgb_image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)
|
|
138
|
+
>>> non_rgb_error(rgb_image) # No error raised
|
|
139
|
+
>>>
|
|
140
|
+
>>> grayscale_image = np.random.randint(0, 256, (100, 100), dtype=np.uint8)
|
|
141
|
+
>>> non_rgb_error(grayscale_image) # Raises ValueError with conversion instructions
|
|
142
|
+
>>>
|
|
143
|
+
>>> multispectral_image = np.random.randint(0, 256, (100, 100, 5), dtype=np.uint8)
|
|
144
|
+
>>> non_rgb_error(multispectral_image) # Raises ValueError stating incompatibility
|
|
145
|
+
|
|
146
|
+
"""
|
|
147
|
+
if not is_rgb_image(image):
|
|
148
|
+
message = "This transformation expects 3-channel images"
|
|
149
|
+
if is_grayscale_image(image):
|
|
150
|
+
message += "\nYou can convert your grayscale image to RGB using cv2.cvtColor(image, cv2.COLOR_GRAY2RGB))"
|
|
151
|
+
if is_multispectral_image(image): # Any image with a number of channels other than 1 and 3
|
|
152
|
+
message += "\nThis transformation cannot be applied to multi-spectral images"
|
|
153
|
+
|
|
154
|
+
raise ValueError(message)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def check_range(value: tuple[float, float], lower_bound: float, upper_bound: float, name: str | None) -> None:
|
|
158
|
+
"""Checks if the given value is within the specified bounds
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
value (tuple[float, float]): The value to check and convert. Can be a single float or a tuple of floats.
|
|
162
|
+
lower_bound (float): The lower bound for the range check.
|
|
163
|
+
upper_bound (float): The upper bound for the range check.
|
|
164
|
+
name (str | None): The name of the parameter being checked. Used for error messages.
|
|
165
|
+
|
|
166
|
+
Raises:
|
|
167
|
+
ValueError: If the value is outside the bounds or if the tuple values are not ordered correctly.
|
|
168
|
+
|
|
169
|
+
"""
|
|
170
|
+
if not all(lower_bound <= x <= upper_bound for x in value):
|
|
171
|
+
raise ValueError(f"All values in {name} must be within [{lower_bound}, {upper_bound}] for tuple inputs.")
|
|
172
|
+
if not value[0] <= value[1]:
|
|
173
|
+
raise ValueError(f"{name!s} tuple values must be ordered as (min, max). Got: {value}")
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class PCA:
|
|
177
|
+
def __init__(self, n_components: int | None = None) -> None:
|
|
178
|
+
if n_components is not None and n_components <= 0:
|
|
179
|
+
raise ValueError("Number of components must be greater than zero.")
|
|
180
|
+
self.n_components = n_components
|
|
181
|
+
self.mean: np.ndarray | None = None
|
|
182
|
+
self.components_: np.ndarray | None = None
|
|
183
|
+
self.explained_variance_: np.ndarray | None = None
|
|
184
|
+
|
|
185
|
+
def fit(self, x: np.ndarray) -> None:
|
|
186
|
+
x = x.astype(np.float64, copy=False) # avoid unnecessary copy if already float64
|
|
187
|
+
n_samples, n_features = x.shape
|
|
188
|
+
|
|
189
|
+
# Determine the number of components if not set
|
|
190
|
+
if self.n_components is None:
|
|
191
|
+
self.n_components = min(n_samples, n_features)
|
|
192
|
+
|
|
193
|
+
self.mean, eigenvectors, eigenvalues = cv2.PCACompute2(x, mean=None, maxComponents=self.n_components)
|
|
194
|
+
self.components_ = eigenvectors
|
|
195
|
+
self.explained_variance_ = eigenvalues.flatten()
|
|
196
|
+
|
|
197
|
+
def transform(self, x: np.ndarray) -> np.ndarray:
|
|
198
|
+
if self.components_ is None:
|
|
199
|
+
raise ValueError(
|
|
200
|
+
"This PCA instance is not fitted yet. "
|
|
201
|
+
"Call 'fit' with appropriate arguments before using this estimator.",
|
|
202
|
+
)
|
|
203
|
+
x = x.astype(np.float64, copy=False) # avoid unnecessary copy if already float64
|
|
204
|
+
return cv2.PCAProject(x, self.mean, self.components_)
|
|
205
|
+
|
|
206
|
+
def fit_transform(self, x: np.ndarray) -> np.ndarray:
|
|
207
|
+
self.fit(x)
|
|
208
|
+
return self.transform(x)
|
|
209
|
+
|
|
210
|
+
def inverse_transform(self, x: np.ndarray) -> np.ndarray:
|
|
211
|
+
if self.components_ is None:
|
|
212
|
+
raise ValueError(
|
|
213
|
+
"This PCA instance is not fitted yet. "
|
|
214
|
+
"Call 'fit' with appropriate arguments before using this estimator.",
|
|
215
|
+
)
|
|
216
|
+
return cv2.PCABackProject(x, self.mean, self.components_)
|
|
217
|
+
|
|
218
|
+
def explained_variance_ratio(self) -> np.ndarray:
|
|
219
|
+
if self.explained_variance_ is None:
|
|
220
|
+
raise ValueError(
|
|
221
|
+
"This PCA instance is not fitted yet. Call 'fit' with appropriate arguments before using this method.",
|
|
222
|
+
)
|
|
223
|
+
total_variance = np.sum(self.explained_variance_)
|
|
224
|
+
return self.explained_variance_ / total_variance
|
|
225
|
+
|
|
226
|
+
def cumulative_explained_variance_ratio(self) -> np.ndarray:
|
|
227
|
+
return np.cumsum(self.explained_variance_ratio())
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def handle_empty_array(param_name: str) -> Callable[[F], F]:
|
|
231
|
+
def decorator(func: F) -> F:
|
|
232
|
+
@functools.wraps(func)
|
|
233
|
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
234
|
+
# Check if the parameter is passed as positional argument
|
|
235
|
+
if len(args) > 0:
|
|
236
|
+
array = args[0]
|
|
237
|
+
# Check if the parameter is passed as keyword argument
|
|
238
|
+
elif param_name in kwargs:
|
|
239
|
+
array = kwargs[param_name]
|
|
240
|
+
else:
|
|
241
|
+
raise ValueError(f"Missing required argument: {param_name}")
|
|
242
|
+
|
|
243
|
+
if len(array) == 0:
|
|
244
|
+
return array
|
|
245
|
+
return func(*args, **kwargs)
|
|
246
|
+
|
|
247
|
+
return cast("F", wrapper)
|
|
248
|
+
|
|
249
|
+
return decorator
|
|
File without changes
|