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,129 @@
|
|
|
1
|
+
"""Module containing validation mechanisms for transform parameters.
|
|
2
|
+
|
|
3
|
+
This module provides a metaclass that enables parameter validation for transforms using
|
|
4
|
+
Pydantic models. It intercepts the initialization of transform classes to validate their
|
|
5
|
+
parameters against schema definitions, raising appropriate errors for invalid values and
|
|
6
|
+
providing type conversion capabilities. This validation layer helps prevent runtime errors
|
|
7
|
+
by catching configuration issues at initialization time.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from inspect import Parameter, signature
|
|
13
|
+
from typing import Any, Callable
|
|
14
|
+
from warnings import warn
|
|
15
|
+
|
|
16
|
+
from pydantic import BaseModel, ValidationError
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ValidatedTransformMeta(type):
|
|
20
|
+
"""Metaclass that validates transform parameters during instantiation.
|
|
21
|
+
|
|
22
|
+
This metaclass enables automatic validation of transform parameters using Pydantic models,
|
|
23
|
+
ensuring proper typing and constraints are enforced before object creation.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
original_init (Callable[..., Any]): Original __init__ method of the class.
|
|
27
|
+
args (tuple[Any, ...]): Positional arguments passed to the __init__ method.
|
|
28
|
+
kwargs (dict[str, Any]): Keyword arguments passed to the __init__ method.
|
|
29
|
+
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
@staticmethod
|
|
33
|
+
def _process_init_parameters(
|
|
34
|
+
original_init: Callable[..., Any],
|
|
35
|
+
args: tuple[Any, ...],
|
|
36
|
+
kwargs: dict[str, Any],
|
|
37
|
+
) -> tuple[dict[str, Any], list[str], bool]:
|
|
38
|
+
init_params = signature(original_init).parameters
|
|
39
|
+
param_names = list(init_params.keys())[1:] # Exclude 'self'
|
|
40
|
+
full_kwargs: dict[str, Any] = dict(zip(param_names, args)) | kwargs
|
|
41
|
+
|
|
42
|
+
# Get strict value before validation
|
|
43
|
+
strict = full_kwargs.pop("strict", False)
|
|
44
|
+
|
|
45
|
+
# Add default values if not provided
|
|
46
|
+
for parameter_name, parameter in init_params.items():
|
|
47
|
+
if (
|
|
48
|
+
parameter_name != "self"
|
|
49
|
+
and parameter_name not in full_kwargs
|
|
50
|
+
and parameter.default is not Parameter.empty
|
|
51
|
+
):
|
|
52
|
+
full_kwargs[parameter_name] = parameter.default
|
|
53
|
+
|
|
54
|
+
return full_kwargs, param_names, strict
|
|
55
|
+
|
|
56
|
+
@staticmethod
|
|
57
|
+
def _validate_parameters(
|
|
58
|
+
schema_cls: type[BaseModel],
|
|
59
|
+
full_kwargs: dict[str, Any],
|
|
60
|
+
param_names: list[str],
|
|
61
|
+
strict: bool,
|
|
62
|
+
) -> dict[str, Any]:
|
|
63
|
+
try:
|
|
64
|
+
# Include strict parameter for schema validation
|
|
65
|
+
schema_kwargs = {k: v for k, v in full_kwargs.items() if k in param_names}
|
|
66
|
+
schema_kwargs["strict"] = strict
|
|
67
|
+
config = schema_cls(**schema_kwargs)
|
|
68
|
+
validated_kwargs = config.model_dump()
|
|
69
|
+
validated_kwargs.pop("strict", None)
|
|
70
|
+
except ValidationError as e:
|
|
71
|
+
raise ValueError(str(e)) from e
|
|
72
|
+
except Exception as e:
|
|
73
|
+
if strict:
|
|
74
|
+
raise ValueError(str(e)) from e
|
|
75
|
+
warn(str(e), stacklevel=2)
|
|
76
|
+
return {}
|
|
77
|
+
else:
|
|
78
|
+
return validated_kwargs
|
|
79
|
+
|
|
80
|
+
@staticmethod
|
|
81
|
+
def _get_default_values(init_params: dict[str, Parameter]) -> dict[str, Any]:
|
|
82
|
+
validated_kwargs = {}
|
|
83
|
+
for param_name, param in init_params.items():
|
|
84
|
+
if param_name in {"self", "strict"}:
|
|
85
|
+
continue
|
|
86
|
+
if param.default is not Parameter.empty:
|
|
87
|
+
validated_kwargs[param_name] = param.default
|
|
88
|
+
return validated_kwargs
|
|
89
|
+
|
|
90
|
+
def __new__(cls: type[Any], name: str, bases: tuple[type, ...], dct: dict[str, Any]) -> type[Any]:
|
|
91
|
+
"""This is a custom metaclass that validates the parameters of the class during instantiation.
|
|
92
|
+
It is used to ensure that the parameters of the class are valid and that they are of the correct type.
|
|
93
|
+
"""
|
|
94
|
+
if "InitSchema" in dct and issubclass(dct["InitSchema"], BaseModel):
|
|
95
|
+
original_init: Callable[..., Any] | None = dct.get("__init__")
|
|
96
|
+
if original_init is None:
|
|
97
|
+
msg = "__init__ not found in class definition"
|
|
98
|
+
raise ValueError(msg)
|
|
99
|
+
|
|
100
|
+
original_sig = signature(original_init)
|
|
101
|
+
|
|
102
|
+
def custom_init(self: Any, *args: Any, **kwargs: Any) -> None:
|
|
103
|
+
full_kwargs, param_names, strict = cls._process_init_parameters(original_init, args, kwargs)
|
|
104
|
+
|
|
105
|
+
validated_kwargs = cls._validate_parameters(
|
|
106
|
+
dct["InitSchema"],
|
|
107
|
+
full_kwargs,
|
|
108
|
+
param_names,
|
|
109
|
+
strict,
|
|
110
|
+
) or cls._get_default_values(signature(original_init).parameters)
|
|
111
|
+
|
|
112
|
+
# Store and check invalid args
|
|
113
|
+
invalid_args = [name_arg for name_arg in kwargs if name_arg not in param_names and name_arg != "strict"]
|
|
114
|
+
original_init(self, **validated_kwargs)
|
|
115
|
+
self.invalid_args = invalid_args
|
|
116
|
+
|
|
117
|
+
if invalid_args:
|
|
118
|
+
message = f"Argument(s) '{', '.join(invalid_args)}' are not valid for transform {name}"
|
|
119
|
+
if strict:
|
|
120
|
+
raise ValueError(message)
|
|
121
|
+
warn(message, stacklevel=2)
|
|
122
|
+
|
|
123
|
+
# Preserve the original signature and docstring
|
|
124
|
+
custom_init.__signature__ = original_sig # type: ignore[attr-defined]
|
|
125
|
+
custom_init.__doc__ = original_init.__doc__
|
|
126
|
+
|
|
127
|
+
dct["__init__"] = custom_init
|
|
128
|
+
|
|
129
|
+
return super().__new__(cls, name, bases, dct)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .transforms import *
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"""Module containing PyTorch-specific transforms for Albumentations.
|
|
2
|
+
|
|
3
|
+
This module provides transforms that convert NumPy arrays to PyTorch tensors in
|
|
4
|
+
the appropriate format. It handles both 2D image data and 3D volumetric data,
|
|
5
|
+
ensuring that the tensor dimensions are correctly arranged according to PyTorch's
|
|
6
|
+
expected format (channels first). These transforms are typically used as the final
|
|
7
|
+
step in an augmentation pipeline before feeding data to a PyTorch model.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from typing import Any, overload
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
import torch
|
|
16
|
+
|
|
17
|
+
from albumentations.core.transforms_interface import BasicTransform
|
|
18
|
+
from albumentations.core.type_definitions import (
|
|
19
|
+
MONO_CHANNEL_DIMENSIONS,
|
|
20
|
+
NUM_MULTI_CHANNEL_DIMENSIONS,
|
|
21
|
+
NUM_VOLUME_DIMENSIONS,
|
|
22
|
+
Targets,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
__all__ = ["ToTensor3D", "ToTensorV2"]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ToTensorV2(BasicTransform):
|
|
29
|
+
"""Converts images/masks to PyTorch Tensors, inheriting from BasicTransform.
|
|
30
|
+
For images:
|
|
31
|
+
- If input is in `HWC` format, converts to PyTorch `CHW` format
|
|
32
|
+
- If input is in `HW` format, converts to PyTorch `1HW` format (adds channel dimension)
|
|
33
|
+
|
|
34
|
+
Attributes:
|
|
35
|
+
transpose_mask (bool): If True, transposes 3D input mask dimensions from `[height, width, num_channels]` to
|
|
36
|
+
`[num_channels, height, width]`.
|
|
37
|
+
p (float): Probability of applying the transform. Default: 1.0.
|
|
38
|
+
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
_targets = (Targets.IMAGE, Targets.MASK)
|
|
42
|
+
|
|
43
|
+
def __init__(self, transpose_mask: bool = False, p: float = 1.0):
|
|
44
|
+
super().__init__(p=p)
|
|
45
|
+
self.transpose_mask = transpose_mask
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def targets(self) -> dict[str, Any]:
|
|
49
|
+
"""Define mapping of target name to target function.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
dict[str, Any]: Dictionary mapping target names to corresponding transform functions.
|
|
53
|
+
|
|
54
|
+
"""
|
|
55
|
+
return {
|
|
56
|
+
"image": self.apply,
|
|
57
|
+
"images": self.apply_to_images,
|
|
58
|
+
"mask": self.apply_to_mask,
|
|
59
|
+
"masks": self.apply_to_masks,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
def apply(self, img: np.ndarray, **params: Any) -> torch.Tensor:
|
|
63
|
+
"""Convert a 2D image array to a PyTorch tensor.
|
|
64
|
+
|
|
65
|
+
Converts image from HWC or HW format to CHW format, handling both
|
|
66
|
+
single-channel and multi-channel images.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
img (np.ndarray): Image as a numpy array of shape (H,W) or (H,W,C)
|
|
70
|
+
**params (Any): Additional parameters
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
torch.Tensor: PyTorch tensor in CHW format
|
|
74
|
+
|
|
75
|
+
Raises:
|
|
76
|
+
ValueError: If image dimensions are neither HW nor HWC
|
|
77
|
+
|
|
78
|
+
"""
|
|
79
|
+
if img.ndim not in {MONO_CHANNEL_DIMENSIONS, NUM_MULTI_CHANNEL_DIMENSIONS}:
|
|
80
|
+
msg = "Albumentations only supports images in HW or HWC format"
|
|
81
|
+
raise ValueError(msg)
|
|
82
|
+
|
|
83
|
+
if img.ndim == MONO_CHANNEL_DIMENSIONS:
|
|
84
|
+
img = np.expand_dims(img, 2)
|
|
85
|
+
|
|
86
|
+
return torch.from_numpy(img.transpose(2, 0, 1))
|
|
87
|
+
|
|
88
|
+
def apply_to_mask(self, mask: np.ndarray, **params: Any) -> torch.Tensor:
|
|
89
|
+
"""Convert a mask array to a PyTorch tensor.
|
|
90
|
+
|
|
91
|
+
If transpose_mask is True and mask has 3 dimensions (H,W,C),
|
|
92
|
+
converts mask to channels-first format (C,H,W).
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
mask (np.ndarray): Mask as a numpy array
|
|
96
|
+
**params (Any): Additional parameters
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
torch.Tensor: PyTorch tensor of mask
|
|
100
|
+
|
|
101
|
+
"""
|
|
102
|
+
if self.transpose_mask and mask.ndim == NUM_MULTI_CHANNEL_DIMENSIONS:
|
|
103
|
+
mask = mask.transpose(2, 0, 1)
|
|
104
|
+
return torch.from_numpy(mask)
|
|
105
|
+
|
|
106
|
+
@overload
|
|
107
|
+
def apply_to_masks(self, masks: list[np.ndarray], **params: Any) -> list[torch.Tensor]: ...
|
|
108
|
+
|
|
109
|
+
@overload
|
|
110
|
+
def apply_to_masks(self, masks: np.ndarray, **params: Any) -> torch.Tensor: ...
|
|
111
|
+
|
|
112
|
+
def apply_to_masks(self, masks: np.ndarray | list[np.ndarray], **params: Any) -> torch.Tensor | list[torch.Tensor]:
|
|
113
|
+
"""Convert numpy array or list of numpy array masks to torch tensor(s).
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
masks (np.ndarray | list[np.ndarray]): Numpy array of shape (N, H, W) or (N, H, W, C),
|
|
117
|
+
or a list of numpy arrays with shape (H, W) or (H, W, C).
|
|
118
|
+
**params (Any): Additional parameters.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
torch.Tensor | list[torch.Tensor]: If transpose_mask is True and input is (N, H, W, C),
|
|
122
|
+
returns tensor of shape (N, C, H, W). If transpose_mask is True and input is (H, W, C), r
|
|
123
|
+
eturns a list of tensors with shape (C, H, W). Otherwise, returns tensors with the same shape as input.
|
|
124
|
+
|
|
125
|
+
"""
|
|
126
|
+
if isinstance(masks, list):
|
|
127
|
+
return [self.apply_to_mask(mask, **params) for mask in masks]
|
|
128
|
+
|
|
129
|
+
if self.transpose_mask and masks.ndim == NUM_VOLUME_DIMENSIONS: # (N, H, W, C)
|
|
130
|
+
masks = np.transpose(masks, (0, 3, 1, 2)) # -> (N, C, H, W)
|
|
131
|
+
return torch.from_numpy(masks)
|
|
132
|
+
|
|
133
|
+
def apply_to_images(self, images: np.ndarray, **params: Any) -> torch.Tensor:
|
|
134
|
+
"""Convert batch of images from (N, H, W, C) to (N, C, H, W)."""
|
|
135
|
+
if images.ndim != NUM_VOLUME_DIMENSIONS: # N,H,W,C
|
|
136
|
+
raise ValueError(f"Expected 4D array (N,H,W,C), got {images.ndim}D array")
|
|
137
|
+
return torch.from_numpy(images.transpose(0, 3, 1, 2)) # -> (N,C,H,W)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class ToTensor3D(BasicTransform):
|
|
141
|
+
"""Convert 3D volumes and masks to PyTorch tensors.
|
|
142
|
+
|
|
143
|
+
This transform is designed for 3D medical imaging data. It converts numpy arrays
|
|
144
|
+
to PyTorch tensors and ensures consistent channel positioning.
|
|
145
|
+
|
|
146
|
+
For all inputs (volumes and masks):
|
|
147
|
+
- Input: (D, H, W, C) or (D, H, W) - depth, height, width, [channels]
|
|
148
|
+
- Output: (C, D, H, W) - channels first format for PyTorch
|
|
149
|
+
For single-channel input, adds C=1 dimension
|
|
150
|
+
|
|
151
|
+
Note:
|
|
152
|
+
This transform always moves channels to first position as this is
|
|
153
|
+
the standard PyTorch format. For masks that need to stay in DHWC format,
|
|
154
|
+
use a different transform or handle the transposition after this transform.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
p (float): Probability of applying the transform. Default: 1.0
|
|
158
|
+
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
_targets = (Targets.IMAGE, Targets.MASK)
|
|
162
|
+
|
|
163
|
+
def __init__(self, p: float = 1.0):
|
|
164
|
+
super().__init__(p=p)
|
|
165
|
+
|
|
166
|
+
@property
|
|
167
|
+
def targets(self) -> dict[str, Any]:
|
|
168
|
+
"""Define mapping of target name to target function.
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
dict[str, Any]: Dictionary mapping target names to corresponding transform functions
|
|
172
|
+
|
|
173
|
+
"""
|
|
174
|
+
return {
|
|
175
|
+
"volume": self.apply_to_volume,
|
|
176
|
+
"mask3d": self.apply_to_mask3d,
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
def apply_to_volume(self, volume: np.ndarray, **params: Any) -> torch.Tensor:
|
|
180
|
+
"""Convert 3D volume to channels-first tensor."""
|
|
181
|
+
if volume.ndim == NUM_VOLUME_DIMENSIONS: # D,H,W,C
|
|
182
|
+
return torch.from_numpy(volume.transpose(3, 0, 1, 2))
|
|
183
|
+
if volume.ndim == NUM_VOLUME_DIMENSIONS - 1: # D,H,W
|
|
184
|
+
return torch.from_numpy(volume[np.newaxis, ...])
|
|
185
|
+
raise ValueError(f"Expected 3D or 4D array (D,H,W) or (D,H,W,C), got {volume.ndim}D array")
|
|
186
|
+
|
|
187
|
+
def apply_to_mask3d(self, mask3d: np.ndarray, **params: Any) -> torch.Tensor:
|
|
188
|
+
"""Convert 3D mask to channels-first tensor."""
|
|
189
|
+
return self.apply_to_volume(mask3d, **params)
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nrtk-albumentations
|
|
3
|
+
Version: 2.1.0
|
|
4
|
+
Summary: Fork of Albumentations in direct support of NRTK (Natural Robustness Toolkit). Fast, flexible, and advanced augmentation library for deep learning, computer vision, and medical imaging. Offers a wide range of transformations for both 2D (images, masks, bboxes, keypoints) and 3D (volumes, volumetric masks, keypoints) data, with optimized performance and seamless integration into ML workflows.
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Keywords: 2D augmentation,3D augmentation,aerial photography,anomaly detection,artificial intelligence,autonomous driving,bounding boxes,classification,computer vision,computer vision library,data augmentation,data preprocessing,data science,deep learning,deep learning library,depth estimation,face recognition,fast augmentation,image augmentation,image processing,image transformation,images,instance segmentation,keras,keypoint detection,keypoints,machine learning,machine learning tools,masks,medical imaging,microscopy,object counting,object detection,optimized performance,panoptic segmentation,pose estimation,python library,pytorch,quality inspection,real-time processing,robotics vision,satellite imagery,semantic segmentation,tensorflow,volumes,volumetric data,volumetric masks
|
|
8
|
+
Author: Vladimir Iglovikov
|
|
9
|
+
Maintainer: Kitware, Inc.
|
|
10
|
+
Maintainer-email: nrtk@kitware.com
|
|
11
|
+
Requires-Python: >=3.9
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Healthcare Industry
|
|
15
|
+
Classifier: Intended Audience :: Information Technology
|
|
16
|
+
Classifier: Intended Audience :: Science/Research
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
27
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
28
|
+
Classifier: Topic :: Scientific/Engineering
|
|
29
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
30
|
+
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
31
|
+
Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
|
|
32
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
33
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
34
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
35
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
36
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
37
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
38
|
+
Classifier: Typing :: Typed
|
|
39
|
+
Provides-Extra: hub
|
|
40
|
+
Provides-Extra: pytorch
|
|
41
|
+
Provides-Extra: text
|
|
42
|
+
Requires-Dist: PyYAML
|
|
43
|
+
Requires-Dist: albucore (==0.0.28)
|
|
44
|
+
Requires-Dist: eval-type-backport ; python_version < "3.10"
|
|
45
|
+
Requires-Dist: numpy (>=1.24.4)
|
|
46
|
+
Requires-Dist: opencv-python-headless (>=4.9.0.80)
|
|
47
|
+
Requires-Dist: pydantic (>=2.9.2)
|
|
48
|
+
Requires-Dist: scipy (>=1.10.0)
|
|
49
|
+
Requires-Dist: typing-extensions (>=4.9.0) ; python_version < "3.10"
|
|
50
|
+
Project-URL: Homepage, https://nrtk.readthedocs.io/
|
|
51
|
+
Project-URL: Repository, https://github.com/Kitware/nrtk-albumentations
|
|
52
|
+
Description-Content-Type: text/markdown
|
|
53
|
+
|
|
54
|
+
# Albumentations (NRTK Fork)
|
|
55
|
+
|
|
56
|
+
[](https://github.com/Kitware/nrtk-albumentations/actions)
|
|
57
|
+
[](https://opensource.org/licenses/MIT)
|
|
58
|
+
|
|
59
|
+
This is a fork of [Albumentations](https://github.com/albumentations-team/albumentations) maintained by Kitware in direct support of [NRTK (Natural Robustness Toolkit)](https://github.com/Kitware/nrtk).
|
|
60
|
+
|
|
61
|
+
**Fork Information:**
|
|
62
|
+
|
|
63
|
+
- Last upstream version: Albumentations v2.0.8
|
|
64
|
+
- Forked: October 2025
|
|
65
|
+
|
|
66
|
+
## About
|
|
67
|
+
|
|
68
|
+
Albumentations is a Python library for fast and flexible image augmentation. This fork is maintained specifically for integration with NRTK and includes modifications to support NRTK's natural robustness evaluation workflows.
|
|
69
|
+
|
|
70
|
+
Image augmentation is used in deep learning and computer vision tasks to increase the quality of trained models by creating new training samples from existing data through various transformations.
|
|
71
|
+
|
|
72
|
+
## Key Features
|
|
73
|
+
|
|
74
|
+
- **Complete Computer Vision Support**: Works with all major CV tasks including classification, segmentation (semantic & instance), object detection, and pose estimation
|
|
75
|
+
- **Simple, Unified API**: One consistent interface for all data types - RGB/grayscale/multispectral images, masks, bounding boxes, and keypoints
|
|
76
|
+
- **Rich Augmentation Library**: 70+ high-quality augmentations to enhance your training data
|
|
77
|
+
- **Fast**: Optimized for production use with consistently high performance
|
|
78
|
+
- **Deep Learning Integration**: Works with PyTorch, TensorFlow, and other frameworks
|
|
79
|
+
- **3D Support**: Volumetric data transformations for medical imaging and other 3D applications
|
|
80
|
+
|
|
81
|
+
## Installation
|
|
82
|
+
|
|
83
|
+
This package is designed to be installed as a dependency of NRTK. For standalone installation, use:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
pip install nrtk-albumentations
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## A Simple Example
|
|
90
|
+
|
|
91
|
+
This package is intended for use with NRTK's [AlbumentationPerturber](https://nrtk.readthedocs.io/en/latest/_implementations/nrtk.impls.perturb_image.generic.albumentations_perturber.AlbumentationsPerturber.html#albumentationsperturber). For direct usage:
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
import albumentations as A
|
|
95
|
+
import cv2
|
|
96
|
+
|
|
97
|
+
# Declare an augmentation pipeline
|
|
98
|
+
transform = A.Compose([
|
|
99
|
+
A.RandomCrop(width=256, height=256),
|
|
100
|
+
A.HorizontalFlip(p=0.5),
|
|
101
|
+
A.RandomBrightnessContrast(p=0.2),
|
|
102
|
+
])
|
|
103
|
+
|
|
104
|
+
# Read an image with OpenCV and convert it to the RGB colorspace
|
|
105
|
+
image = cv2.imread("image.jpg")
|
|
106
|
+
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
|
107
|
+
|
|
108
|
+
# Augment an image
|
|
109
|
+
transformed = transform(image=image)
|
|
110
|
+
transformed_image = transformed["image"]
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Documentation
|
|
114
|
+
|
|
115
|
+
For detailed documentation on available transforms and usage patterns, see:
|
|
116
|
+
|
|
117
|
+
- [Albumentations (NRTK Fork) Documentation](https://nrtk-albumentations.readthedocs.io/)
|
|
118
|
+
- [NRTK Documentation](https://nrtk.readthedocs.io/)
|
|
119
|
+
|
|
120
|
+
## Available Transformations
|
|
121
|
+
|
|
122
|
+
Albumentations provides 70+ transforms across several categories:
|
|
123
|
+
|
|
124
|
+
### Pixel-level Transforms
|
|
125
|
+
|
|
126
|
+
Transforms that modify pixel values without changing image geometry (masks, bboxes, keypoints remain unchanged):
|
|
127
|
+
|
|
128
|
+
- Color adjustments: `RandomBrightnessContrast`, `HueSaturationValue`, `ColorJitter`
|
|
129
|
+
- Noise addition: `GaussNoise`, `ISONoise`, `MultiplicativeNoise`
|
|
130
|
+
- Blur effects: `GaussianBlur`, `MotionBlur`, `MedianBlur`, `Defocus`
|
|
131
|
+
- Compression: `ImageCompression`
|
|
132
|
+
- And many more...
|
|
133
|
+
|
|
134
|
+
### Spatial-level Transforms
|
|
135
|
+
|
|
136
|
+
Transforms that modify image geometry (automatically applied to masks, bboxes, keypoints):
|
|
137
|
+
|
|
138
|
+
- Cropping: `RandomCrop`, `CenterCrop`, `RandomResizedCrop`
|
|
139
|
+
- Flipping: `HorizontalFlip`, `VerticalFlip`
|
|
140
|
+
- Rotation: `Rotate`, `RandomRotate90`, `SafeRotate`
|
|
141
|
+
- Resizing: `Resize`, `LongestMaxSize`, `SmallestMaxSize`
|
|
142
|
+
- Distortions: `ElasticTransform`, `GridDistortion`, `OpticalDistortion`
|
|
143
|
+
- And many more...
|
|
144
|
+
|
|
145
|
+
### 3D Transforms
|
|
146
|
+
|
|
147
|
+
Transforms for volumetric data (medical imaging, etc.):
|
|
148
|
+
|
|
149
|
+
- `RandomCrop3D`, `CenterCrop3D`
|
|
150
|
+
- `Pad3D`, `PadIfNeeded3D`
|
|
151
|
+
- `CoarseDropout3D`
|
|
152
|
+
- `CubicSymmetry`
|
|
153
|
+
|
|
154
|
+
For a complete list with detailed parameters, see the [transform reference](https://nrtk-albumentations.readthedocs.io/).
|
|
155
|
+
|
|
156
|
+
## Maintainer
|
|
157
|
+
|
|
158
|
+
**Kitware, Inc.** <nrtk@kitware.com>
|
|
159
|
+
|
|
160
|
+
## Original Authors
|
|
161
|
+
|
|
162
|
+
This library was originally created by:
|
|
163
|
+
|
|
164
|
+
- Vladimir I. Iglovikov
|
|
165
|
+
- Alexander Buslaev
|
|
166
|
+
- Alex Parinov
|
|
167
|
+
- Eugene Khvedchenya
|
|
168
|
+
- Mikhail Druzhinin
|
|
169
|
+
|
|
170
|
+
## Contributing
|
|
171
|
+
|
|
172
|
+
This fork is maintained specifically for NRTK integration. We are not accepting general contributions at this time. For issues or questions related to NRTK integration, please open an issue on the [NRTK repository](https://github.com/Kitware/nrtk/issues).
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
177
|
+
|
|
178
|
+
## Citation
|
|
179
|
+
|
|
180
|
+
If you use this library in your research, please consider citing the original Albumentations paper:
|
|
181
|
+
|
|
182
|
+
```bibtex
|
|
183
|
+
@Article{info11020125,
|
|
184
|
+
AUTHOR = {Buslaev, Alexander and Iglovikov, Vladimir I. and Khvedchenya, Eugene and Parinov, Alex and Druzhinin, Mikhail and Kalinin, Alexandr A.},
|
|
185
|
+
TITLE = {Albumentations: Fast and Flexible Image Augmentations},
|
|
186
|
+
JOURNAL = {Information},
|
|
187
|
+
VOLUME = {11},
|
|
188
|
+
YEAR = {2020},
|
|
189
|
+
NUMBER = {2},
|
|
190
|
+
ARTICLE-NUMBER = {125},
|
|
191
|
+
URL = {https://www.mdpi.com/2078-2489/11/2/125},
|
|
192
|
+
ISSN = {2078-2489},
|
|
193
|
+
DOI = {10.3390/info11020125}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
albumentations/__init__.py,sha256=Sk7dqtYmpts6J7IMOSKw4ujwnH_w5NUBzfzcztCkNcI,573
|
|
2
|
+
albumentations/augmentations/__init__.py,sha256=qHQ0t3fYFf-qXQkKN7LyMnEaPzvIGajqJzh8K8NyGw4,782
|
|
3
|
+
albumentations/augmentations/blur/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
albumentations/augmentations/blur/functional.py,sha256=bO5oiliKqL7p98YOe2Zl7ldD66zoxyTz2u7VexpQJBs,13776
|
|
5
|
+
albumentations/augmentations/blur/transforms.py,sha256=12rI4oQSZymIex5WwncGhqs-YveDg844lBsq-77BU8s,64630
|
|
6
|
+
albumentations/augmentations/crops/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
albumentations/augmentations/crops/functional.py,sha256=bfrv8IhTjKXoow-dYeqEW5k4iX4GK2w1SrbqauW_22w,16903
|
|
8
|
+
albumentations/augmentations/crops/transforms.py,sha256=m-6xXx7r1kM7v9yxhdnXybZ4jCJKZPswi0SJHL-k6NU,148240
|
|
9
|
+
albumentations/augmentations/dropout/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
albumentations/augmentations/dropout/channel_dropout.py,sha256=xoIhL5rb6bKjBYGrzHhukSQ5PMUY9zECKClPFEvX8l0,5275
|
|
11
|
+
albumentations/augmentations/dropout/coarse_dropout.py,sha256=4zD1J2aPnSbFB1OFm3Vao45VBYpWa90baYzU6vbPtwo,24492
|
|
12
|
+
albumentations/augmentations/dropout/functional.py,sha256=Wpv4P6pQHN3rjbtUtiBq-7gcLuVnezQieGRbYCsD230,38362
|
|
13
|
+
albumentations/augmentations/dropout/grid_dropout.py,sha256=jG4pa4ZbmSnwCKlxRBJi6QBO5gDO6zo4v5BoDkGeerU,7261
|
|
14
|
+
albumentations/augmentations/dropout/mask_dropout.py,sha256=L6x2LPLbjq3CM_ZfXUcgG97AZH8bEL4mpX1PxA3Zx5w,10962
|
|
15
|
+
albumentations/augmentations/dropout/transforms.py,sha256=8sZnobY574Y-z81OzYUf8ArWu4UvupNXbGa4UzjFo1g,18273
|
|
16
|
+
albumentations/augmentations/dropout/xy_masking.py,sha256=_0L6aW752rCOP8ADvYWD4aJk7guAEVSZVbJYXTTBq4s,8097
|
|
17
|
+
albumentations/augmentations/geometric/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
+
albumentations/augmentations/geometric/distortion.py,sha256=YQ5vkrNd4bZUrZDuKJFm6u9PIlfG3g8EYIPgYm-kISg,48045
|
|
19
|
+
albumentations/augmentations/geometric/flip.py,sha256=2ZVlFhDSg6Mn8miMOtRemTy2u0G4De_8G7gsOrPjM9o,25203
|
|
20
|
+
albumentations/augmentations/geometric/functional.py,sha256=PorxaiJJMDAH7Lms55AIttB4FIdOOHvq9ve6J0oI-h8,146597
|
|
21
|
+
albumentations/augmentations/geometric/pad.py,sha256=JkuUslEFgYYmd-BA-Z0xYDhGbVYbUGZCybo4T1JOJtY,25372
|
|
22
|
+
albumentations/augmentations/geometric/resize.py,sha256=7pOnGX6zzC-2kRgRBpCbBRzmtIciPCZTCag12G2sgKU,39492
|
|
23
|
+
albumentations/augmentations/geometric/rotate.py,sha256=3I20GCBL65jWmBDkf4_qFFw9r33SuFdxxpddhRKrrFM,32685
|
|
24
|
+
albumentations/augmentations/geometric/transforms.py,sha256=KcxMFVrCm0GcukS40QEnf1BsPP5Ga4Rk2CQCTJMaT0M,76125
|
|
25
|
+
albumentations/augmentations/mixing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
|
+
albumentations/augmentations/mixing/domain_adaptation.py,sha256=AjT-awcV0tghUGf6Tey7McWGpYa15B0TAyDMQPHXJAs,34558
|
|
27
|
+
albumentations/augmentations/mixing/domain_adaptation_functional.py,sha256=epgaxvSls0Edo3kMZ8Vdr_pmKShXjdX1SEq1sOF2qlw,18010
|
|
28
|
+
albumentations/augmentations/mixing/functional.py,sha256=6VfJaGlGc95FBfAPF0atfGYMiAKJw0YGVXFS2NYdELA,34942
|
|
29
|
+
albumentations/augmentations/mixing/transforms.py,sha256=q0mHPxZOF1C8uCEHPyzdpUw5kd7G5gCTOTQGor_Hv48,34380
|
|
30
|
+
albumentations/augmentations/other/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
|
+
albumentations/augmentations/other/lambda_transform.py,sha256=Lvvv4shQCPlOR3Fjr46iuK-2vQTA4rNwJKxI9JhCk4c,6520
|
|
32
|
+
albumentations/augmentations/other/type_transform.py,sha256=1PuYIAzufKmkim898xmgGQP9y8Lsir_EQXNxFJbBuww,9595
|
|
33
|
+
albumentations/augmentations/pixel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
|
+
albumentations/augmentations/pixel/functional.py,sha256=-_ZuO3g2YkiBWmBHvKnFnVaXqJouO-OeB87u9u-TRZQ,141544
|
|
35
|
+
albumentations/augmentations/pixel/transforms.py,sha256=Co0mqLYEHL9jG3n3TiFXf3Pl8N33yfLc7UWrxBHa0mE,291028
|
|
36
|
+
albumentations/augmentations/spectrogram/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
37
|
+
albumentations/augmentations/spectrogram/transform.py,sha256=9EhuF4NlLS0DSJkTQ5EA2_1N3kEHXfrGf3GJj1qrkiw,7356
|
|
38
|
+
albumentations/augmentations/text/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
39
|
+
albumentations/augmentations/text/functional.py,sha256=WOwNlwAlJH7Y33HWwaFiPezHn0TPcGgtOzW4b0BEQMo,10106
|
|
40
|
+
albumentations/augmentations/text/transforms.py,sha256=Nco6PlvP0K5ErRlwHxAjRC88LdIe-HxtjTIEWeIp6bA,10991
|
|
41
|
+
albumentations/augmentations/transforms3d/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
42
|
+
albumentations/augmentations/transforms3d/functional.py,sha256=McXenIxg2lXr19QxKX-BGpcZMkDS4r_CNf_MIkXKGmI,16163
|
|
43
|
+
albumentations/augmentations/transforms3d/transforms.py,sha256=357bbh-D4qyJHoH_y7tDctFF1I8zyN966gD6jVHOZGk,56596
|
|
44
|
+
albumentations/augmentations/utils.py,sha256=IWTwpDtc7fsMUj5PBPUuS-YUlay-WOTuvHLWqlEnVYU,9296
|
|
45
|
+
albumentations/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
46
|
+
albumentations/core/bbox_utils.py,sha256=8ew3J42OhyFK1TimJMZbz2H165UUVkEF0EjzPd4xOp0,35659
|
|
47
|
+
albumentations/core/composition.py,sha256=IzEEEdqQnEdNF4RdIsGAr6fs_4rIlXs6BT6W6Egp0_o,76503
|
|
48
|
+
albumentations/core/hub_mixin.py,sha256=eQWqcb6f6GylX3kkQuXjn5b82K1wFebv-waEXsjU_oI,12479
|
|
49
|
+
albumentations/core/keypoints_utils.py,sha256=4RvQcBH6i8kFNgaW7cCGUra8DUPUSBjeV3lWC10hCUg,19089
|
|
50
|
+
albumentations/core/label_manager.py,sha256=1QQo3RexpnGwc3K0QHJ7RBVN9qG6XsWX-mwcNwZORAs,13082
|
|
51
|
+
albumentations/core/pydantic.py,sha256=O-csH5n1h04gigOz0GxcZJAPSpGY2bzhrt10YU3jVHY,7645
|
|
52
|
+
albumentations/core/serialization.py,sha256=yWcudkzgbRGrv7mbpgBfRQJlQBXZvrv4NzetnzC0BgI,14794
|
|
53
|
+
albumentations/core/transforms_interface.py,sha256=gCfatqo_fSNkGI59kK6wBMZgZHZHf6Zp82uPkYjiAWc,37138
|
|
54
|
+
albumentations/core/type_definitions.py,sha256=3d7Cpx78vGRuvcqygVF7BP8mOlDlg-9E4NH8d6JPyRE,3594
|
|
55
|
+
albumentations/core/utils.py,sha256=CHVA47nW6C93JdXmO8k7_8D1uwJ30HNZN9LMOxBGDqY,20913
|
|
56
|
+
albumentations/core/validation.py,sha256=PCfiqa-K-Jvu5NCcAp0r-NnDPnoBmJbUNeAoxFABPfk,5383
|
|
57
|
+
albumentations/pytorch/__init__.py,sha256=_FX2n_Gzz9oGUCHqoQlo4ez2EBuXzhuDHDot-h2SCME,26
|
|
58
|
+
albumentations/pytorch/transforms.py,sha256=PZ1MCraVEOdduxe6hykKzMITvHT02H3Ak76Ewpx-MRA,7237
|
|
59
|
+
nrtk_albumentations-2.1.0.dist-info/METADATA,sha256=AqZdOV74fSNCe3-Q7qgGYDeMOCm99dKk3m7hjMFrgfg,8697
|
|
60
|
+
nrtk_albumentations-2.1.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
61
|
+
nrtk_albumentations-2.1.0.dist-info/licenses/LICENSE,sha256=vqTcjpOuJ4S8zUXxzbpT2pe5lka8o5DH1yXhe3LcIYA,1114
|
|
62
|
+
nrtk_albumentations-2.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2017 Vladimir Iglovikov, Alexander Buslaev, Alexander Parinov,
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|